28 Commits
1.0.4 ... 1.0.7

Author SHA1 Message Date
Shazron Abdullah
4f9afb7499 Update version to 1.0.7 2014-05-09 16:53:41 -07:00
Shazron Abdullah
f7283216ef Merge pull request #35 from wjywbs/remove-app
Fixes #35 - Add uninstall feature.
2014-05-09 16:51:37 -07:00
wjywbs
cfb086ee44 Add uninstall feature. 2014-05-09 16:02:51 -04:00
Shazron Abdullah
9e52fd16d7 Added CONTRIBUTING file 2014-05-01 23:11:25 -07:00
Shazron Abdullah
6c42fd3cf9 Merge pull request #33 from Unity-Technologies/master
Fix for lldb's "^D" and "quit" spam when running without a terminal.
2014-04-29 17:35:50 -07:00
Julius Trinkunas
3ee1bca513 Stop lldb's "^D" and "quit" spam in a non terminal environment. 2014-04-29 14:33:00 +03:00
Julius Trinkunas
0a9418a564 Add friendly message for code-signing check fails. 2014-04-29 14:33:00 +03:00
Shazron Abdullah
72e83d06e1 Merge pull request #27 from oNaiPs/better_sdk_makefile
Better sdk makefile
2014-04-03 12:35:22 -07:00
Shazron Abdullah
bab1d5bded Merge pull request #26 from oNaiPs/fix_402653028_error
Added version to Info.plist file, fixes -402653028 error
2014-04-03 12:34:40 -07:00
Jose Pereira
f0d5d58a8b Separated sdk version from full path, easier to set after updates 2014-04-03 11:45:57 -07:00
Jose Pereira
8fc098a877 Added version to Info.plist file, fixes -402653028 error 2014-04-03 11:42:57 -07:00
Shazron Abdullah
bad5fe321b Merge pull request #25 from Unity-Technologies/master
Fix exiting and propagate app exit code in non interactive mode.
2014-04-02 10:35:05 -07:00
Julius Trinkunas
79c4cc5c6a Fix exiting and propagate app exit code in non interactive mode. 2014-04-01 16:20:14 +03:00
Shazron Abdullah
f54f67c418 Added the two new flags from 1.0.6 to the README 2014-03-27 15:32:19 -07:00
Shazron Abdullah
f1a2c40566 Increment version to 1.0.6 2014-03-27 15:11:35 -07:00
Shazron Abdullah
c92682d148 Merge pull request #22 from Unity-Technologies/master
Add non interactive mode.
2014-03-27 12:35:25 -07:00
Julius Trinkunas
08f3b92306 Fix hangup when launching from scripts. 2014-03-27 16:14:14 +02:00
Julius Trinkunas
a4f3c2c84b Add non interactive mode. 2014-03-26 19:13:48 +02:00
Julius Trinkunas
a3a99e6553 Remove python wrapper shell. 2014-03-26 18:59:01 +02:00
wjywbs
7f2231bd39 Read port number in command line to debug app in multiple devices. 2014-03-24 14:37:03 -07:00
Shazron Abdullah
ad7c97d01f Updated version to 1.0.5 2014-03-20 17:03:52 -07:00
Shazron Abdullah
fa88a73a9a Fixes #20 - lldb startup super-slow unless ios-deploy.c is edited to match exact xcode version 2014-03-20 17:00:32 -07:00
Shazron Abdullah
83150622c2 Merge pull request #13 from zgramana/master
Fixes phonegap/ios-deploy#8.
2013-12-13 16:34:43 -08:00
Zachary Gramana
42f41798a6 Fixes phonegap/ios-deploy#8. 2013-12-13 16:02:14 -08:00
Shazron Abdullah
486d0d92e0 Merge pull request #12 from zgramana/master
Enabled LLDB interactive session. Perf improvement.
2013-12-12 13:39:43 -08:00
Zachary Gramana
c97735aaee Added back support for -n/--nostart option. 2013-12-10 11:18:13 -08:00
Zachary Gramana
d7c38e9e9f Enabled LLDB interactive session. Perf improvement. 2013-12-09 17:59:19 -08:00
Shazron Abdullah
6f78fbc5f3 Update README.md 2013-10-25 15:43:59 -07:00
7 changed files with 355 additions and 68 deletions

31
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,31 @@
## Contributing to ios-deploy
Github url:
https://github.com/phonegap/ios-deploy
Git clone url:
https://github.com/phonegap/ios-deploy.git
## Filing an issue
Please run the commands below in your Terminal.app and include it in the issue:
```
1. sw_vers -productVersion
2. ios-deploy -V
3. xcodebuild -version
4. xcode-select --print-path
5. gcc --version
6. lldb --version
```
Also include **command line arguments** you used for ios-deploy.
## Sending a Pull Request
Please **create a topic branch** for your issue before submitting your pull request. You will be asked to re-submit if your pull request contains unrelated commits.
Please elaborate regarding the problem the pull request is supposed to solve, and perhaps also link to any relevant issues the pull request is trying to fix.

View File

@@ -10,6 +10,8 @@
</array>
<key>CFBundleExecutable</key>
<string>demo</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundleIdentifier</key>
<string>demo</string>
<key>CFBundleResourceSpecification</key>

View File

@@ -1,6 +1,8 @@
IOS_CC = gcc
IOS_SDK = $(shell xcode-select --print-path)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk
IOS_SDK_VERSION = 7.1
IOS_CC = gcc -ObjC
DEVICE_SUPPORT = $(shell xcode-select --print-path)/Platforms/iPhoneOS.platform/DeviceSupport
IOS_SDK = $(shell xcode-select --print-path)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$(IOS_SDK_VERSION).sdk
all: clean ios-deploy
@@ -11,10 +13,10 @@ demo.app: demo Info.plist
codesign -f -s "iPhone Developer" --entitlements Entitlements.plist demo.app
demo: demo.c
$(IOS_CC) -arch armv7 -isysroot $(IOS_SDK) -framework CoreFoundation -o demo demo.c
$(IOS_CC) -g -arch armv7 -isysroot $(IOS_SDK) -framework CoreFoundation -o demo demo.c
ios-deploy: clean ios-deploy.c
$(IOS_CC) -o ios-deploy -framework CoreFoundation -framework MobileDevice -F/System/Library/PrivateFrameworks ios-deploy.c
$(IOS_CC) -g -o ios-deploy -framework Foundation -framework CoreFoundation -framework MobileDevice -F/System/Library/PrivateFrameworks ios-deploy.c
symlink:
cd $(DEVICE_SUPPORT); ln -sfn "`find . -type d -maxdepth 1 -exec basename {} \; | tail -1`" Latest
@@ -30,4 +32,4 @@ debug: all
./ios-deploy --debug --bundle demo.app
clean:
rm -rf *.app demo ios-deploy
rm -rf *.app demo ios-deploy

View File

@@ -453,6 +453,8 @@ typedef int (*am_device_install_application_callback)(CFDictionaryRef, int);
mach_error_t AMDeviceInstallApplication(service_conn_t socket, CFStringRef path, CFDictionaryRef options, am_device_install_application_callback callback, void *user);
mach_error_t AMDeviceTransferApplication(service_conn_t socket, CFStringRef path, CFDictionaryRef options, am_device_install_application_callback callbackj, void *user);
int AMDeviceSecureUninstallApplication(int unknown0, struct am_device *device, CFStringRef bundle_id, int unknown1, void *callback, int callback_arg);
/* ----------------------------------------------------------------------------
* Semi-private routines
* ------------------------------------------------------------------------- */

View File

@@ -10,20 +10,22 @@ Install and debug iPhone apps without using Xcode. Designed to work on unjailbro
## Usage
./ios-deploy [OPTION]...
-d, --debug launch the app in GDB after installation
-i, --id <device_id> the id of the device to connect to
-c, --detect only detect if the device is connected
-b, --bundle <bundle.app> the path to the app bundle to be installed
-a, --args <args> command line arguments to pass to the app when launching it
-t, --timeout <timeout> number of seconds to wait for a device to be connected
-u, --unbuffered don't buffer stdout
-g, --gdbargs <args> extra arguments to pass to GDB when starting the debugger
-x, --gdbexec <file> GDB commands script file
-n, --nostart do not start the app when debugging
-v, --verbose enable verbose output
-m, --noinstall directly start debugging without app install (-d not required)
-V, --version print the executable version
./ios-deploy [OPTION]...
-d, --debug launch the app in GDB after installation
-i, --id <device_id> the id of the device to connect to
-c, --detect only detect if the device is connected
-b, --bundle <bundle.app> the path to the app bundle to be installed
-a, --args <args> command line arguments to pass to the app when launching it
-t, --timeout <timeout> number of seconds to wait for a device to be connected
-u, --unbuffered don't buffer stdout
-g, --gdbargs <args> extra arguments to pass to GDB when starting the debugger
-x, --gdbexec <file> GDB commands script file
-n, --nostart do not start the app when debugging
-I, --noninteractive start in non interactive mode (quit when app crashes or exits)
-v, --verbose enable verbose output
-m, --noinstall directly start debugging without app install (-d not required)
-p, --port <number> port used for device, default: 12345
-V, --version print the executable version
## Demo

View File

@@ -1,6 +1,7 @@
//TODO: don't copy/mount DeveloperDiskImage.dmg if it's already done - Xcode checks this somehow
#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -15,9 +16,9 @@
#include <netinet/tcp.h>
#include "MobileDevice.h"
#define APP_VERSION "1.0.4"
#define APP_VERSION "1.0.7"
#define PREP_CMDS_PATH "/tmp/fruitstrap-lldb-prep-cmds-"
#define LLDB_SHELL "python -u -c \"import time; time.sleep(0.5); print 'run'; time.sleep(2000000)\" | lldb -s " PREP_CMDS_PATH
#define LLDB_SHELL "lldb -s " PREP_CMDS_PATH
/*
* Startup script passed to lldb.
@@ -26,14 +27,28 @@
* log enable -v -f /Users/vargaz/gdb-remote.log gdb-remote all
*/
#define LLDB_PREP_CMDS CFSTR("\
platform select remote-ios --sysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/*\n\
platform select remote-ios --sysroot {symbols_path}\n\
target create \"{disk_app}\"\n\
script fruitstrap_device_app=\"{device_app}\"\n\
script fruitstrap_connect_url=\"connect://127.0.0.1:12345\"\n\
script fruitstrap_handle_command=\"command script add -s asynchronous -f {python_command}.fsrun_command run\"\n\
script fruitstrap_connect_url=\"connect://127.0.0.1:{device_port}\"\n\
command script import \"{python_file_path}\"\n\
command script add -f {python_command}.connect_command connect\n\
command script add -s asynchronous -f {python_command}.run_command run\n\
command script add -s asynchronous -f {python_command}.autoexit_command autoexit\n\
connect\n\
")
const char* lldb_prep_no_cmds = "";
const char* lldb_prep_interactive_cmds = "\
run\n\
";
const char* lldb_prep_noninteractive_cmds = "\
run\n\
autoexit\n\
";
/*
* Some things do not seem to work when using the normal commands like process connect/launch, so we invoke them
* through the python interface. Also, Launch () doesn't seem to work when ran from init_module (), so we add
@@ -41,37 +56,94 @@
*/
#define LLDB_FRUITSTRAP_MODULE CFSTR("\
import lldb\n\
import sys\n\
\n\
def __lldb_init_module(debugger, internal_dict):\n\
def connect_command(debugger, command, result, internal_dict):\n\
# These two are passed in by the script which loads us\n\
device_app=internal_dict['fruitstrap_device_app']\n\
connect_url=internal_dict['fruitstrap_connect_url']\n\
handle_command = internal_dict['fruitstrap_handle_command']\n\
lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))\n\
lldb.debugger.HandleCommand(handle_command)\n\
error=lldb.SBError()\n\
lldb.target.ConnectRemote(lldb.target.GetDebugger().GetListener(),connect_url,None,error)\n\
connect_url = internal_dict['fruitstrap_connect_url']\n\
error = lldb.SBError()\n\
\n\
def fsrun_command(debugger, command, result, internal_dict):\n\
error=lldb.SBError()\n\
lldb.target.Launch(lldb.SBLaunchInfo(['{args}']),error)\n\
process = lldb.target.ConnectRemote(lldb.target.GetDebugger().GetListener(), connect_url, None, error)\n\
\n\
# Wait for connection to succeed\n\
listener = lldb.target.GetDebugger().GetListener()\n\
listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged)\n\
events = []\n\
state = lldb.eStateInvalid\n\
while state != lldb.eStateConnected:\n\
event = lldb.SBEvent()\n\
if listener.WaitForEvent(1, event):\n\
state = process.GetStateFromEvent(event)\n\
events.append(event)\n\
else:\n\
state = lldb.eStateInvalid\n\
\n\
# Add events back to queue, otherwise lldb freezes\n\
for event in events:\n\
listener.AddEvent(event)\n\
\n\
def run_command(debugger, command, result, internal_dict):\n\
device_app = internal_dict['fruitstrap_device_app']\n\
error = lldb.SBError()\n\
lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))\n\
lldb.target.Launch(lldb.SBLaunchInfo(['{args}']), error)\n\
print str(error)\n\
\n\
def autoexit_command(debugger, command, result, internal_dict):\n\
process = lldb.target.process\n\
listener = debugger.GetListener()\n\
listener.StartListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)\n\
event = lldb.SBEvent()\n\
while True:\n\
if listener.WaitForEvent(1, event):\n\
state = process.GetStateFromEvent(event)\n\
else:\n\
state = lldb.eStateInvalid\n\
\n\
stdout = process.GetSTDOUT(1024)\n\
while stdout:\n\
sys.stdout.write(stdout)\n\
stdout = process.GetSTDOUT(1024)\n\
\n\
stderr = process.GetSTDERR(1024)\n\
while stderr:\n\
sys.stdout.write(stderr)\n\
stderr = process.GetSTDERR(1024)\n\
\n\
if lldb.SBProcess.EventIsProcessEvent(event):\n\
if state == lldb.eStateExited:\n\
sys.exit(process.GetExitStatus())\n\
if state == lldb.eStateStopped:\n\
debugger.HandleCommand('frame select')\n\
debugger.HandleCommand('bt')\n\
sys.exit({exitcode_app_crash})\n\
")
typedef struct am_device * AMDeviceRef;
int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg);
int AMDeviceLookupApplications(AMDeviceRef device, int zero, CFDictionaryRef* result);
mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result);
bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, detect_only = false, install = true;
bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, detect_only = false, install = true, uninstall = false;
bool interactive = true;
char *app_path = NULL;
char *device_id = NULL;
char *args = NULL;
int timeout = 0;
int port = 12345;
CFStringRef last_path = NULL;
service_conn_t gdbfd;
pid_t parent = 0;
// PID of child process running lldb
pid_t child = 0;
// Signal sent from child to parent process when LLDB finishes.
const int SIGLLDB = SIGUSR1;
// Error codes we report on different failures, so scripts can distinguish between user app exit
// codes and our exit codes. For non app errors we use codes in reserved 128-255 range.
const int exitcode_error = 253;
const int exitcode_app_crash = 254;
Boolean path_exists(CFTypeRef path) {
if (CFGetTypeID(path) == CFStringGetTypeID()) {
@@ -104,7 +176,7 @@ CFStringRef find_path(CFStringRef rootPath, CFStringRef namePattern, CFStringRef
if (!(fpipe = (FILE *)popen(command, "r")))
{
perror("Error encountered while opening pipe");
exit(EXIT_FAILURE);
exit(exitcode_error);
}
char buffer[256] = { '\0' };
@@ -129,7 +201,7 @@ CFStringRef copy_xcode_dev_path() {
if (!(fpipe = (FILE *)popen(command, "r")))
{
perror("Error encountered while opening pipe");
exit(EXIT_FAILURE);
exit(exitcode_error);
}
char buffer[256] = { '\0' };
@@ -235,7 +307,7 @@ CFStringRef copy_device_support_path(AMDeviceRef device) {
if (path == NULL)
{
printf("[ !! ] Unable to locate DeviceSupport directory.\n[ !! ] This probably means you don't have Xcode installed, you will need to launch the app manually and logging output will not be shown!\n");
exit(1);
exit(exitcode_error);
}
return path;
@@ -260,7 +332,7 @@ CFStringRef copy_developer_disk_image_path(CFStringRef deviceSupportPath) {
if (path == NULL)
{
printf("[ !! ] Unable to locate DeveloperDiskImage.dmg.\n[ !! ] This probably means you don't have Xcode installed, you will need to launch the app manually and logging output will not be shown!\n");
exit(1);
exit(exitcode_error);
}
return path;
@@ -308,7 +380,7 @@ void mount_developer_image(AMDeviceRef device) {
printf("[ 95%%] Developer disk image already mounted\n");
} else {
printf("[ !! ] Unable to mount developer disk image. (%x)\n", result);
exit(1);
exit(exitcode_error);
}
CFRelease(image_path);
@@ -346,8 +418,38 @@ mach_error_t install_callback(CFDictionaryRef dict, int arg) {
}
CFURLRef copy_device_app_url(AMDeviceRef device, CFStringRef identifier) {
CFDictionaryRef result;
assert(AMDeviceLookupApplications(device, 0, &result) == 0);
CFDictionaryRef result = nil;
NSArray *a = [NSArray arrayWithObjects:
@"CFBundleIdentifier", // absolute must
@"ApplicationDSID",
@"ApplicationType",
@"CFBundleExecutable",
@"CFBundleDisplayName",
@"CFBundleIconFile",
@"CFBundleName",
@"CFBundleShortVersionString",
@"CFBundleSupportedPlatforms",
@"CFBundleURLTypes",
@"CodeInfoIdentifier",
@"Container",
@"Entitlements",
@"HasSettingsBundle",
@"IsUpgradeable",
@"MinimumOSVersion",
@"Path",
@"SignerIdentity",
@"UIDeviceFamily",
@"UIFileSharingEnabled",
@"UIStatusBarHidden",
@"UISupportedInterfaceOrientations",
nil];
NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"];
CFDictionaryRef options = (CFDictionaryRef)optionsDict;
afc_error_t resultStatus = AMDeviceLookupApplications(device, options, &result);
assert(resultStatus == 0);
CFDictionaryRef app_dict = CFDictionaryGetValue(result, identifier);
assert(app_dict != NULL);
@@ -376,21 +478,32 @@ CFStringRef copy_disk_app_identifier(CFURLRef disk_app_url) {
}
void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) {
CFStringRef ds_path = copy_device_support_path(device);
CFStringRef symbols_path = CFStringCreateWithFormat(NULL, NULL, CFSTR("'%@/Symbols'"), ds_path);
CFMutableStringRef cmds = CFStringCreateMutableCopy(NULL, 0, LLDB_PREP_CMDS);
CFRange range = { 0, CFStringGetLength(cmds) };
CFStringRef ds_path = copy_device_support_path(device);
CFStringFindAndReplace(cmds, CFSTR("{symbols_path}"), symbols_path, range, 0);
range.length = CFStringGetLength(cmds);
CFStringFindAndReplace(cmds, CFSTR("{ds_path}"), ds_path, range, 0);
range.length = CFStringGetLength(cmds);
CFMutableStringRef pmodule = CFStringCreateMutableCopy(NULL, 0, LLDB_FRUITSTRAP_MODULE);
CFRange rangeLLDB = { 0, CFStringGetLength(pmodule) };
CFStringRef exitcode_app_crash_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), exitcode_app_crash);
CFStringFindAndReplace(pmodule, CFSTR("{exitcode_app_crash}"), exitcode_app_crash_str, rangeLLDB, 0);
rangeLLDB.length = CFStringGetLength(pmodule);
if (args) {
CFStringRef cf_args = CFStringCreateWithCString(NULL, args, kCFStringEncodingASCII);
CFStringFindAndReplace(cmds, CFSTR("{args}"), cf_args, range, 0);
//format the arguments 'arg1 arg2 ....' to an argument list ['arg1', 'arg2', ...]
CFMutableStringRef argsListLLDB = CFStringCreateMutableCopy(NULL, 0, cf_args);
CFRange rangeLLDB = { 0, CFStringGetLength(argsListLLDB) };
rangeLLDB.length = CFStringGetLength(argsListLLDB);
CFStringFindAndReplace(argsListLLDB, CFSTR(" "), CFSTR(" "), rangeLLDB, 0);//remove multiple spaces
rangeLLDB.length = CFStringGetLength(argsListLLDB);
CFStringFindAndReplace(argsListLLDB, CFSTR(" "), CFSTR("', '"), rangeLLDB, 0);
@@ -413,6 +526,10 @@ void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) {
CFStringFindAndReplace(cmds, CFSTR("{disk_app}"), disk_app_path, range, 0);
range.length = CFStringGetLength(cmds);
CFStringRef device_port = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), port);
CFStringFindAndReplace(cmds, CFSTR("{device_port}"), device_port, range, 0);
range.length = CFStringGetLength(cmds);
CFURLRef device_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, device_app_url);
CFStringRef device_container_path = CFURLCopyFileSystemPath(device_container_url, kCFURLPOSIXPathStyle);
CFMutableStringRef dcp_noprivate = CFStringCreateMutableCopy(NULL, 0, device_container_path);
@@ -447,6 +564,15 @@ void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) {
strcat(prep_cmds_path, device_id);
FILE *out = fopen(prep_cmds_path, "w");
fwrite(CFDataGetBytePtr(cmds_data), CFDataGetLength(cmds_data), 1, out);
// Write additional commands based on mode we're running in
const char* extra_cmds;
if (!interactive)
extra_cmds = lldb_prep_noninteractive_cmds;
else if (nostart)
extra_cmds = lldb_prep_no_cmds;
else
extra_cmds = lldb_prep_interactive_cmds;
fwrite(extra_cmds, strlen(extra_cmds), 1, out);
fclose(out);
CFDataRef pmodule_data = CFStringCreateExternalRepresentation(NULL, pmodule, kCFStringEncodingASCII, 0);
@@ -535,7 +661,7 @@ void start_remote_debug_server(AMDeviceRef device) {
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(12345);
addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
CFSocketRef fdvendor = CFSocketCreate(NULL, PF_INET, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL);
@@ -599,7 +725,27 @@ void killed(int signum) {
void lldb_finished_handler(int signum)
{
_exit(0);
int status = 0;
if (waitpid(child, &status, 0) == -1)
perror("waitpid failed");
_exit(WEXITSTATUS(status));
}
void bring_process_to_foreground() {
if (setpgid(0, 0) == -1)
perror("setpgid failed");
signal(SIGTTOU, SIG_IGN);
if (tcsetpgrp(STDIN_FILENO, getpid()) == -1)
perror("tcsetpgrp failed");
signal(SIGTTOU, SIG_DFL);
}
void setup_dummy_pipe_on_stdin(int pfd[2]) {
if (pipe(pfd) == -1)
perror("pipe failed");
if (dup2(pfd[0], STDIN_FILENO) == -1)
perror("dup2 failed");
}
void launch_debugger(AMDeviceRef device, CFURLRef url) {
@@ -619,22 +765,88 @@ void launch_debugger(AMDeviceRef device, CFURLRef url) {
printf("[100%%] Connecting to remote debug server\n");
printf("-------------------------\n");
signal(SIGHUP, lldb_finished_handler);
setpgid(getpid(), 0);
signal(SIGHUP, killed);
signal(SIGINT, killed);
signal(SIGTERM, killed);
// Need this before fork to avoid race conditions. For child process we remove this right after fork.
signal(SIGLLDB, lldb_finished_handler);
parent = getpid();
int pid = fork();
if (pid == 0) {
char lldb_shell[300] = LLDB_SHELL;
signal(SIGHUP, SIG_DFL);
signal(SIGLLDB, SIG_DFL);
child = getpid();
int pfd[2] = {-1, -1};
if (isatty(STDIN_FILENO))
// If we are running on a terminal, then we need to bring process to foreground for input
// to work correctly on lldb's end.
bring_process_to_foreground();
else
// If lldb is running in a non terminal environment, then it freaks out spamming "^D" and
// "quit". It seems this is caused by read() on stdin returning EOF in lldb. To hack around
// this we setup a dummy pipe on stdin, so read() would block expecting "user's" input.
setup_dummy_pipe_on_stdin(pfd);
char lldb_shell[400];
sprintf(lldb_shell, LLDB_SHELL);
if(device_id != NULL)
strcat(lldb_shell, device_id);
system(lldb_shell); // launch lldb
kill(parent, SIGHUP); // "No. I am your father."
_exit(0);
}
int status = system(lldb_shell); // launch lldb
if (status == -1)
perror("failed launching lldb");
close(pfd[0]);
close(pfd[1]);
// Notify parent we're exiting
kill(parent, SIGLLDB);
// Pass lldb exit code
_exit(WEXITSTATUS(status));
} else if (pid > 0) {
child = pid;
} else {
perror("fork failed");
exit(exitcode_error);
}
}
CFStringRef get_bundle_id(CFURLRef app_url)
{
if (app_url == NULL)
return NULL;
CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, app_url, CFSTR("Info.plist"), false);
if (url == NULL)
return NULL;
CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, url);
CFRelease(url);
if (stream == NULL)
return NULL;
CFPropertyListRef plist = NULL;
if (CFReadStreamOpen(stream) == TRUE) {
plist = CFPropertyListCreateWithStream(NULL, stream, 0,
kCFPropertyListImmutable, NULL, NULL);
}
CFReadStreamClose(stream);
CFRelease(stream);
if (plist == NULL)
return NULL;
const void *value = CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier"));
CFStringRef bundle_id = NULL;
if (value != NULL)
bundle_id = CFRetain(value);
CFRelease(plist);
return bundle_id;
}
void handle_device(AMDeviceRef device) {
@@ -665,7 +877,29 @@ void handle_device(AMDeviceRef device) {
CFRelease(relative_url);
if (uninstall) {
printf("------ Uninstall phase ------\n");
CFStringRef bundle_id = get_bundle_id(url);
if (bundle_id == NULL) {
printf("Error: Unable to get bundle id from package %s\n Uninstall failed\n", app_path);
} else {
AMDeviceConnect(device);
assert(AMDeviceIsPaired(device));
assert(AMDeviceValidatePairing(device) == 0);
assert(AMDeviceStartSession(device) == 0);
assert(AMDeviceSecureUninstallApplication(0, device, bundle_id, 0, NULL, 0) == 0);
assert(AMDeviceStopSession(device) == 0);
assert(AMDeviceDisconnect(device) == 0);
printf("[ OK ] Uninstalled package with bundle id %s\n", CFStringGetCStringPtr(bundle_id, CFStringGetSystemEncoding()));
}
}
if(install) {
printf("------ Install phase ------\n");
printf("[ 0%%] Found device (%s), beginning install\n", CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding()));
AMDeviceConnect(device);
@@ -701,8 +935,11 @@ void handle_device(AMDeviceRef device) {
mach_error_t result = AMDeviceInstallApplication(installFd, path, options, install_callback, NULL);
if (result != 0)
{
printf("AMDeviceInstallApplication failed: %d\n", result);
exit(1);
char* error = "Unknown error.";
if (result == 0xe8008015)
error = "Your application failed code-signing checks. Check your certificates, provisioning profiles, and bundle ids.";
printf("AMDeviceInstallApplication failed: 0x%X: %s\n", result, error);
exit(exitcode_error);
}
close(installFd);
@@ -729,7 +966,7 @@ void device_callback(struct am_device_notification_callback_info *info, void *ar
void timeout_callback(CFRunLoopTimerRef timer, void *info) {
if (!found_device) {
printf("[....] Timed out waiting for device.\n");
exit(1);
exit(exitcode_error);
}
}
@@ -746,8 +983,11 @@ void usage(const char* app) {
" -g, --gdbargs <args> extra arguments to pass to GDB when starting the debugger\n"
" -x, --gdbexec <file> GDB commands script file\n"
" -n, --nostart do not start the app when debugging\n"
" -I, --noninteractive start in non interactive mode (quit when app crashes or exits)\n"
" -v, --verbose enable verbose output\n"
" -m, --noinstall directly start debugging without app install (-d not required) \n"
" -m, --noinstall directly start debugging without app install (-d not required)\n"
" -p, --port <number> port used for device, default: 12345 \n"
" -r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared) \n"
" -V, --version print the executable version \n",
app);
}
@@ -767,14 +1007,17 @@ int main(int argc, char *argv[]) {
{ "gdbexec", no_argument, NULL, 'x' },
{ "unbuffered", no_argument, NULL, 'u' },
{ "nostart", no_argument, NULL, 'n' },
{ "noninteractive", no_argument, NULL, 'I' },
{ "detect", no_argument, NULL, 'c' },
{ "version", no_argument, NULL, 'V' },
{ "noinstall", no_argument, NULL, 'm' },
{ "port", required_argument, NULL, 'p' },
{ "uninstall", no_argument, NULL, 'r' },
{ NULL, 0, NULL, 0 },
};
char ch;
while ((ch = getopt_long(argc, argv, "Vmcdvuni:b:a:t:g:x:", longopts, NULL)) != -1)
while ((ch = getopt_long(argc, argv, "VmcdvunrIi:b:a:t:g:x:p:", longopts, NULL)) != -1)
{
switch (ch) {
case 'm':
@@ -805,21 +1048,30 @@ int main(int argc, char *argv[]) {
case 'n':
nostart = 1;
break;
case 'I':
interactive = false;
break;
case 'c':
detect_only = true;
break;
case 'V':
show_version();
return 1;
return exitcode_error;
case 'p':
port = atoi(optarg);
break;
case 'r':
uninstall = 1;
break;
default:
usage(argv[0]);
return 1;
return exitcode_error;
}
}
if (!app_path && !detect_only) {
usage(argv[0]);
exit(0);
exit(exitcode_error);
}
if (unbuffered) {
@@ -831,10 +1083,6 @@ int main(int argc, char *argv[]) {
timeout = 5;
}
if (!detect_only) {
printf("------ Install phase ------\n");
}
if (app_path) {
assert(access(app_path, F_OK) == 0);
}

View File

@@ -1,7 +1,7 @@
{
"name": "ios-deploy",
"version": "1.0.4",
"description": "launch iOS apps iOS devices from the command line (Xcode 4)",
"version": "1.0.7",
"description": "launch iOS apps iOS devices from the command line (Xcode 5)",
"main": "ios-deploy",
"scripts": {
"preinstall": "make ios-deploy"