17 Commits
1.0.6 ... 1.0.8

Author SHA1 Message Date
Shazron Abdullah
6fc2ee5e38 Updated npm version to 1.0.8 2014-05-12 18:09:05 -07:00
Shazron Abdullah
443abe4d38 Updated version to 1.0.8 2014-05-12 18:05:45 -07:00
Shazron Abdullah
42a7dbcc1f Updated README for --uninstall option. 2014-05-09 16:57:46 -07:00
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
7 changed files with 215 additions and 33 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_SDK_VERSION = 7.1
IOS_CC = gcc -ObjC
IOS_SDK = $(shell xcode-select --print-path)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk
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

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

@@ -21,8 +21,11 @@ Install and debug iPhone apps without using Xcode. Designed to work on unjailbro
-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
-r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared)
-V, --version print the executable version
## Demo

View File

@@ -16,7 +16,7 @@
#include <netinet/tcp.h>
#include "MobileDevice.h"
#define APP_VERSION "1.0.6"
#define APP_VERSION "1.0.8"
#define PREP_CMDS_PATH "/tmp/fruitstrap-lldb-prep-cmds-"
#define LLDB_SHELL "lldb -s " PREP_CMDS_PATH
@@ -34,6 +34,7 @@
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\
")
@@ -44,10 +45,8 @@ const char* lldb_prep_interactive_cmds = "\
";
const char* lldb_prep_noninteractive_cmds = "\
settings set -- auto-confirm true\n\
target stop-hook add --one-liner \"bt\"\n\
target stop-hook add --one-liner \"quit\"\n\
run\n\
autoexit\n\
";
/*
@@ -57,6 +56,7 @@ const char* lldb_prep_noninteractive_cmds = "\
*/
#define LLDB_FRUITSTRAP_MODULE CFSTR("\
import lldb\n\
import sys\n\
\n\
def connect_command(debugger, command, result, internal_dict):\n\
# These two are passed in by the script which loads us\n\
@@ -88,6 +88,35 @@ def run_command(debugger, command, result, internal_dict):\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;
@@ -96,7 +125,7 @@ int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url,
int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg);
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;
@@ -106,6 +135,15 @@ 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()) {
@@ -138,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' };
@@ -163,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' };
@@ -269,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;
@@ -294,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;
@@ -342,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);
@@ -453,13 +491,19 @@ void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) {
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);
@@ -681,7 +725,10 @@ 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() {
@@ -694,6 +741,13 @@ void bring_process_to_foreground() {
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) {
AMDeviceConnect(device);
assert(AMDeviceIsPaired(device));
@@ -711,26 +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) {
bring_process_to_foreground();
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) {
@@ -761,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);
@@ -797,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);
@@ -825,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);
}
}
@@ -846,6 +987,7 @@ void usage(const char* app) {
" -v, --verbose enable verbose output\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);
}
@@ -870,11 +1012,12 @@ int main(int argc, char *argv[]) {
{ "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, "VmcdvunIi:b:a:t:g:x:p:", longopts, NULL)) != -1)
while ((ch = getopt_long(argc, argv, "VmcdvunrIi:b:a:t:g:x:p:", longopts, NULL)) != -1)
{
switch (ch) {
case 'm':
@@ -913,19 +1056,22 @@ int main(int argc, char *argv[]) {
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) {
@@ -937,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.6",
"description": "launch iOS apps iOS devices from the command line (Xcode 4)",
"version": "1.0.8",
"description": "launch iOS apps iOS devices from the command line (Xcode 5)",
"main": "ios-deploy",
"scripts": {
"preinstall": "make ios-deploy"