diff options
Diffstat (limited to 'emrun')
-rwxr-xr-x[-rw-r--r--] | emrun | 437 |
1 files changed, 301 insertions, 136 deletions
@@ -8,6 +8,7 @@ import os, platform, optparse, logging, re, pprint, atexit, urlparse, subprocess from operator import itemgetter from urllib import unquote from Queue import PriorityQueue +from threading import Thread, RLock # Populated from cmdline params emrun_options = None @@ -41,6 +42,9 @@ processname_killed_atexit = "" # If user does not specify a --port parameter, this port is used to launch the server. default_webserver_port = 6931 +# Location of Android Debug Bridge executable +ADB = '' + # Host OS detection to autolocate browsers and other OS-specific support needs. WINDOWS = False LINUX = False @@ -73,7 +77,7 @@ last_message_time = time.clock() page_start_time = time.clock() # Stores the time of most recent http page serve. -page_last_served_time = time.clock() +page_last_served_time = None # Returns given log message formatted to be outputted on a HTML page. def format_html(msg): @@ -82,21 +86,14 @@ def format_html(msg): msg = cgi.escape(msg) msg = msg.replace('\r\n', '<br />').replace('\n', '<br />') return msg - + +# HTTP requests are handled from separate threads - synchronize them to avoid race conditions +http_mutex = RLock() + # Prints a log message to 'info' stdout channel. Always printed. def logi(msg): global last_message_time - if emrun_options.log_html: - sys.stdout.write(format_html(msg)) - else: - print >> sys.stdout, msg - sys.stdout.flush() - last_message_time = time.clock() - -# Prints a verbose log message to stdout channel. Only shown if run with --verbose. -def logv(msg): - global emrun_options, last_message_time - if emrun_options.verbose: + with http_mutex: if emrun_options.log_html: sys.stdout.write(format_html(msg)) else: @@ -104,15 +101,28 @@ def logv(msg): sys.stdout.flush() last_message_time = time.clock() +# Prints a verbose log message to stdout channel. Only shown if run with --verbose. +def logv(msg): + global emrun_options, last_message_time + with http_mutex: + if emrun_options.verbose: + if emrun_options.log_html: + sys.stdout.write(format_html(msg)) + else: + print >> sys.stdout, msg + sys.stdout.flush() + last_message_time = time.clock() + # Prints an error message to stderr channel. def loge(msg): global last_message_time - if emrun_options.log_html: - sys.stderr.write(format_html(msg)) - else: - print >> sys.stderr, msg - sys.stderr.flush() - last_message_time = time.clock() + with http_mutex: + if emrun_options.log_html: + sys.stderr.write(format_html(msg)) + else: + print >> sys.stderr, msg + sys.stderr.flush() + last_message_time = time.clock() def format_eol(msg): if WINDOWS: @@ -122,8 +132,6 @@ def format_eol(msg): # Prints a message to the browser stdout output stream. def browser_logi(msg): global browser_stdout_handle - if browser_stdout_handle != sys.stdout and not msg.endswith('\n'): - msg += '\n' msg = format_eol(msg) print >> browser_stdout_handle, msg browser_stdout_handle.flush() @@ -132,8 +140,6 @@ def browser_logi(msg): # Prints a message to the browser stderr output stream. def browser_loge(msg): global browser_stderr_handle - if browser_stderr_handle != sys.stderr and not msg.endswith('\n'): - msg += '\n' msg = format_eol(msg) print >> browser_stderr_handle, msg browser_stderr_handle.flush() @@ -153,7 +159,7 @@ def is_browser_process_alive(): # Kills browser_process and processname_killed_atexit. def kill_browser_process(): - global browser_process, processname_killed_atexit + global browser_process, processname_killed_atexit, emrun_options, ADB if browser_process: try: logv('Terminating browser process..') @@ -161,21 +167,25 @@ def kill_browser_process(): except: pass browser_process = None - if len(processname_killed_atexit) > 0: - logv("Terminating all processes that have string '" + processname_killed_atexit + "' in their name.") - if WINDOWS: - process_image = processname_killed_atexit if '.exe' in processname_killed_atexit else (processname_killed_atexit + '.exe') - process = subprocess.Popen(['taskkill', '/F', '/IM', process_image, '/T'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - process.communicate() + if emrun_options.android: + logv("Terminating Android app '" + processname_killed_atexit + "'.") + subprocess.call([ADB, 'shell', 'am', 'force-stop', processname_killed_atexit]) else: - try: - subprocess.call(['pkill', processname_killed_atexit]) - except OSError, e: + logv("Terminating all processes that have string '" + processname_killed_atexit + "' in their name.") + if WINDOWS: + process_image = processname_killed_atexit if '.exe' in processname_killed_atexit else (processname_killed_atexit + '.exe') + process = subprocess.Popen(['taskkill', '/F', '/IM', process_image, '/T'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process.communicate() + else: try: - subprocess.call(['killall', processname_killed_atexit]) + subprocess.call(['pkill', processname_killed_atexit]) except OSError, e: - loge('Both commands pkill and killall failed to clean up the spawned browser process. Perhaps neither of these utilities is available on your system?') + try: + subprocess.call(['killall', processname_killed_atexit]) + except OSError, e: + loge('Both commands pkill and killall failed to clean up the spawned browser process. Perhaps neither of these utilities is available on your system?') + # Clear the process name to represent that the browser is now dead. processname_killed_atexit = '' # Our custom HTTP web server that will server the target page to run via .html. @@ -190,56 +200,60 @@ class HTTPWebServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): def handle_incoming_message(self, seq_num, log, data): global have_received_messages - have_received_messages = True - - if self.expected_http_seq_num == -1: - self.expected_http_seq_num = seq_num+1 - log(data) - elif seq_num == -1: # Message arrived without a sequence number? Just log immediately - log(data) - elif seq_num == self.expected_http_seq_num: - log(data) - self.expected_http_seq_num += 1 - self.print_messages_due() - elif seq_num < self.expected_http_seq_num: - log(data) - else: - log(data) - self.http_message_queue += [(seq_num, data, log)] - self.http_message_queue.sort(key=itemgetter(0)) - if len(self.http_message_queue) > 16: - self.print_next_message() + with http_mutex: + have_received_messages = True + + if self.expected_http_seq_num == -1: + self.expected_http_seq_num = seq_num+1 + log(data) + elif seq_num == -1: # Message arrived without a sequence number? Just log immediately + log(data) + elif seq_num == self.expected_http_seq_num: + log(data) + self.expected_http_seq_num += 1 + self.print_messages_due() + elif seq_num < self.expected_http_seq_num: + log(data) + else: + self.http_message_queue += [(seq_num, data, log)] + self.http_message_queue.sort(key=itemgetter(0)) + if len(self.http_message_queue) > 16: + self.print_next_message() # If it's been too long since we we got a message, prints out the oldest queued message, ignoring the proper order. # This ensures that if any messages are actually lost, that the message queue will be orderly flushed. def print_timed_out_messages(self): global last_message_time - now = time.clock() - max_message_queue_time = 5 - if len(self.http_message_queue) > 0 and now - last_message_time > max_message_queue_time: - self.print_next_message() + with http_mutex: + now = time.clock() + max_message_queue_time = 5 + if len(self.http_message_queue) > 0 and now - last_message_time > max_message_queue_time: + self.print_next_message() # Skips to printing the next message in queue now, independent of whether there was missed messages in the sequence numbering. def print_next_message(self): - if len(self.http_message_queue) > 0: - self.expected_http_seq_num = self.http_message_queue[0][0] - self.print_messages_due() + with http_mutex: + if len(self.http_message_queue) > 0: + self.expected_http_seq_num = self.http_message_queue[0][0] + self.print_messages_due() # Completely flushes all out-of-order messages in the queue. def print_all_messages(self): - while len(self.http_message_queue) > 0: - self.print_next_message() + with http_mutex: + while len(self.http_message_queue) > 0: + self.print_next_message() # Prints any messages that are now due after we logged some other previous messages. def print_messages_due(self): - while len(self.http_message_queue) > 0: - msg = self.http_message_queue[0] - if msg[0] == self.expected_http_seq_num: - msg[2](msg[1]) - self.expected_http_seq_num += 1 - self.http_message_queue.pop(0) - else: - return + with http_mutex: + while len(self.http_message_queue) > 0: + msg = self.http_message_queue[0] + if msg[0] == self.expected_http_seq_num: + msg[2](msg[1]) + self.expected_http_seq_num += 1 + self.http_message_queue.pop(0) + else: + return def serve_forever(self, timeout=0.5): global emrun_options, last_message_time, page_exit_code, have_received_messages, emrun_not_enabled_nag_printed @@ -278,10 +292,11 @@ class HTTPWebServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): page_exit_code = emrun_options.timeout_returncode # If we detect that the page is not running with emrun enabled, print a warning message. - time_since_page_serve = now - page_last_served_time - if not emrun_not_enabled_nag_printed and not have_received_messages and time_since_page_serve > 5: - logi('The html page you are running is not emrun-capable. Stdout, stderr and exit(returncode) capture will not work. Recompile the application with the --emrun linker flag to enable this, or pass --no_emrun_detect to emrun to hide this check.') - emrun_not_enabled_nag_printed = True + if not emrun_not_enabled_nag_printed and page_last_served_time is not None: + time_since_page_serve = now - page_last_served_time + if not have_received_messages and time_since_page_serve > 10: + logi('The html page you are running is not emrun-capable. Stdout, stderr and exit(returncode) capture will not work. Recompile the application with the --emrun linker flag to enable this, or pass --no_emrun_detect to emrun to hide this check.') + emrun_not_enabled_nag_printed = True # Clean up at quit, print any leftover messages in queue. self.print_all_messages() @@ -411,6 +426,19 @@ def get_cpu_infoline(): return platform.machine() + ', ' + cpu_name +def get_android_cpu_infoline(): + lines = subprocess.check_output([ADB, 'shell', 'cat', '/proc/cpuinfo']).split('\n') + processor = '' + hardware = '' + for line in lines: + if line.startswith('Processor'): + processor = line[line.find(':')+1:].strip() + elif line.startswith('Hardware'): + hardware = line[line.find(':')+1:].strip() + + freq = int(subprocess.check_output([ADB, 'shell', 'cat', '/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq']).strip())/1000 + return 'CPU: ' + processor + ', ' + hardware + ' @ ' + str(freq) + ' MHz' + def win_print_gpu_info(): gpus = [] gpu_memory = [] @@ -575,19 +603,24 @@ def get_os_version(): return 'Unknown OS' def get_system_memory(): + global emrun_options + try: - if WINDOWS: - return win32api.GlobalMemoryStatusEx()['TotalPhys'] - elif OSX: - return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']).strip()) - elif LINUX: - mem = open('/proc/meminfo', 'r') - lines = mem.readlines() - mem.close() + if LINUX or emrun_options.android: + if emrun_options.android: + lines = subprocess.check_output([ADB, 'shell', 'cat', '/proc/meminfo']).split('\n') + else: + mem = open('/proc/meminfo', 'r') + lines = mem.readlines() + mem.close() for i in lines: sline = i.split() if str(sline[0]) == 'MemTotal:': return int(sline[1]) * 1024 + elif WINDOWS: + return win32api.GlobalMemoryStatusEx()['TotalPhys'] + elif OSX: + return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']).strip()) except: return -1 @@ -698,6 +731,75 @@ def find_browser(name): return None # Could not find the browser +def get_android_model(): + global ADB + manufacturer = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.manufacturer']).strip() + brand = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.brand']).strip() + model = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.model']).strip() + board = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.board']).strip() + device = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.device']).strip() + name = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.name']).strip() + return manufacturer + ' ' + brand + ' ' + model + ' ' + board + ' ' + device + ' ' + name + +def get_android_os_version(): + global ADB + ver = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.release']).strip() + apiLevel = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.sdk']).strip() + if not apiLevel: + apiLevel = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.sdk_int']).strip() + + os = '' + if ver: + os += 'Android ' + ver + ' ' + if apiLevel: + os += 'SDK API Level ' + apiLevel + ' ' + os += subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.description']).strip() + return os + +def list_android_browsers(): + global ADB + apps = subprocess.check_output([ADB, 'shell', 'pm', 'list', 'packages', '-f']).replace('\r\n', '\n') + browsers = [] + for line in apps.split('\n'): + line = line.strip() + if line.endswith('=org.mozilla.firefox'): + browsers += ['firefox'] + if line.endswith('=org.mozilla.firefox_beta'): + browsers += ['firefox_beta'] + if line.endswith('=org.mozilla.fennec_aurora'): + browsers += ['firefox_aurora'] + if line.endswith('=org.mozilla.fennec'): + browsers += ['firefox_nightly'] + if line.endswith('=com.android.chrome'): + browsers += ['chrome'] + if line.endswith('=com.chrome.beta'): + browsers += ['chrome_beta'] + if line.endswith('=com.opera.browser'): + browsers += ['opera'] + if line.endswith('=com.opera.mini.android'): + browsers += ['opera_mini'] + if line.endswith('=mobi.mgeek.TunnyBrowser'): + browsers += ['dolphin'] + + browsers.sort() + logi('emrun has automatically found the following browsers on the connected Android device:') + for browser in browsers: + logi(' - ' + browser) + +def list_pc_browsers(): + browsers = ['firefox', 'firefox_beta', 'firefox_aurora', 'firefox_nightly', 'chrome', 'chrome_canary', 'iexplore', 'safari', 'opera'] + logi('emrun has automatically found the following browsers in the default install locations on the system:') + logi('') + for browser in browsers: + browser_exe = find_browser(browser) + if type(browser_exe) == list: + browser_exe = browser_exe[0] + if browser_exe: + logi(' - ' + browser + ': ' + browser_display_name(browser_exe) + ' ' + get_executable_version(browser_exe)) + logi('') + logi('You can pass the --browser <id> option to launch with the given browser above.') + logi('Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.') + def browser_display_name(browser): b = browser.lower() if 'iexplore' in b: @@ -718,7 +820,7 @@ def browser_display_name(browser): return browser def main(): - global browser_process, processname_killed_atexit, emrun_options, emrun_not_enabled_nag_printed + global browser_process, processname_killed_atexit, emrun_options, emrun_not_enabled_nag_printed, ADB usage_str = "usage: %prog [options] [optional_portnum]" parser = optparse.OptionParser(usage=usage_str) @@ -773,6 +875,9 @@ def main(): parser.add_option('--browser', dest='browser', default='', help='Specifies the browser executable to run the web page in.') + parser.add_option('--android', dest='android', action='store_true', default=False, + help='Launches the page in a browser of an Android device connected to an USB on the local system. (via adb)') + parser.add_option('--system_info', dest='system_info', action='store_true', help='Prints information about the current system at startup.') @@ -785,7 +890,13 @@ def main(): (options, args) = parser.parse_args(sys.argv) emrun_options = options - if not options.browser: + if options.android: + ADB = which('adb') + if not ADB: + loge("Could not find the adb tool. Install Android SDK and add the directory of adb to PATH.") + return 1 + + if not options.browser and not options.android: if WINDOWS: options.browser = 'start' elif LINUX: @@ -795,19 +906,11 @@ def main(): elif OSX: options.browser = 'safari' - browsers = ['firefox', 'firefox_beta', 'firefox_aurora', 'firefox_nightly', 'chrome', 'chrome_canary', 'iexplore', 'safari', 'opera'] if options.list_browsers: - logi('emrun has automatically found the following browsers in the default install locations on the system:') - logi('') - for browser in browsers: - browser_exe = find_browser(browser) - if type(browser_exe) == list: - browser_exe = browser_exe[0] - if browser_exe: - logi(' - ' + browser + ': ' + browser_display_name(browser_exe) + ' ' + get_executable_version(browser_exe)) - logi('') - logi('You can pass the --browser <id> option to launch with the given browser above.') - logi('Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.') + if options.android: + list_android_browsers() + else: + list_pc_browsers() return if len(args) < 2 and (options.system_info or options.browser_info): @@ -827,58 +930,109 @@ def main(): url = os.path.relpath(os.path.abspath(file_to_serve), serve_dir) if len(cmdlineparams) > 0: url += '?' + '&'.join(cmdlineparams) - url = 'http://localhost:'+str(options.port)+'/'+url + server_root = 'localhost' + if options.android: + server_root = socket.gethostbyname(socket.gethostname()) + url = 'http://' + server_root + ':' + str(options.port)+'/'+url os.chdir(serve_dir) logv('Web server root directory: ' + os.path.abspath('.')) - browser = find_browser(str(options.browser)) - browser_exe = browser[0] - browser_args = [] - - if 'safari' in browser_exe.lower(): - # Safari has a bug that a command line 'Safari http://page.com' does not launch that page, - # but instead launches 'file:///http://page.com'. To remedy this, must use the open -a command - # to run Safari, but unfortunately this will end up spawning Safari process detached from emrun. - if OSX: - browser = ['open', '-a', 'Safari'] + (browser[1:] if len(browser) > 1 else []) - - processname_killed_atexit = 'Safari' - elif 'chrome' in browser_exe.lower(): - processname_killed_atexit = 'chrome' - browser_args = ['--incognito', '--enable-nacl', '--enable-pnacl', '--disable-restore-session-state', '--enable-webgl', '--no-default-browser-check', '--no-first-run', '--allow-file-access-from-files'] -# if options.no_server: -# browser_args += ['--disable-web-security'] - elif 'firefox' in browser_exe.lower(): - processname_killed_atexit = 'firefox' - elif 'iexplore' in browser_exe.lower(): - processname_killed_atexit = 'iexplore' - browser_args = ['-private'] - elif 'opera' in browser_exe.lower(): - processname_killed_atexit = 'opera' - - # In Windows cmdline, & character delimits multiple commmands, so must use ^ to escape them. - if browser_exe == 'cmd': - url = url.replace('&', '^&') - browser += browser_args + [url] + if options.android: + if not options.no_browser: + if not options.browser: + loge("Running on Android requires that you explicitly specify the browser to run with --browser <id>. Run emrun --android --list_browsers to obtain a list of installed browsers you can use.") + return 1 + elif options.browser == 'firefox': + browser_app = 'org.mozilla.firefox/.App' + elif options.browser == 'firefox_beta': + browser_app = 'org.mozilla.firefox_beta/.App' + elif options.browser == 'firefox_aurora' or options.browser == 'fennec_aurora': + browser_app = 'org.mozilla.fennec_aurora/.App' + elif options.browser == 'firefox_nightly' or options.browser == 'fennec': + browser_app = 'org.mozilla.fennec/.App' + elif options.browser == 'chrome': + browser_app = 'com.android.chrome/.Main' + elif options.browser == 'chrome_beta' or options.browser == 'chrome_canary': # There is no Chrome Canary for Android, but Play store has 'Chrome Beta' instead. + browser_app = 'com.chrome.beta/com.android.chrome.Main' + elif options.browser == 'opera': + browser_app = 'com.opera.browser/com.opera.Opera' + elif options.browser == 'opera_mini': # Launching the URL works, but page seems to never load (Fails with 'Network problem' even when other browsers work) + browser_app = 'com.opera.mini.android/.Browser' + elif options.browser =='dolphin': # Current stable Dolphin as of 12/2013 does not have WebGL support. + browser_app = 'mobi.mgeek.TunnyBrowser/.BrowserActivity' + else: + loge("Don't know how to launch browser " + options.browser + ' on Android!') + return 1 + # To add support for a new Android browser in the list above: + # 1. Install the browser to Android phone, connect it via adb to PC. + # 2. Type 'adb shell pm list packages -f' to locate the package name of that application. + # 3. Type 'adb pull <packagename>.apk' to copy the apk of that application to PC. + # 4. Type 'aapt d xmltree <packagename>.apk AndroidManifest.xml > manifest.txt' to extract the manifest from the package. + # 5. Locate the name of the main activity for the browser in manifest.txt and add an entry to above list in form 'appname/mainactivityname' + + if WINDOWS: + url = url.replace('&', '\\&') + browser = [ADB, 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-n', browser_app, '-d', url] + processname_killed_atexit = browser_app[:browser_app.find('/')] + else: #Launching a web page on local system. + browser = find_browser(str(options.browser)) + browser_exe = browser[0] + browser_args = [] + + if 'safari' in browser_exe.lower(): + # Safari has a bug that a command line 'Safari http://page.com' does not launch that page, + # but instead launches 'file:///http://page.com'. To remedy this, must use the open -a command + # to run Safari, but unfortunately this will end up spawning Safari process detached from emrun. + if OSX: + browser = ['open', '-a', 'Safari'] + (browser[1:] if len(browser) > 1 else []) + + processname_killed_atexit = 'Safari' + elif 'chrome' in browser_exe.lower(): + processname_killed_atexit = 'chrome' + browser_args = ['--incognito', '--enable-nacl', '--enable-pnacl', '--disable-restore-session-state', '--enable-webgl', '--no-default-browser-check', '--no-first-run', '--allow-file-access-from-files'] + # if options.no_server: + # browser_args += ['--disable-web-security'] + elif 'firefox' in browser_exe.lower(): + processname_killed_atexit = 'firefox' + elif 'iexplore' in browser_exe.lower(): + processname_killed_atexit = 'iexplore' + browser_args = ['-private'] + elif 'opera' in browser_exe.lower(): + processname_killed_atexit = 'opera' + + # In Windows cmdline, & character delimits multiple commmands, so must use ^ to escape them. + if browser_exe == 'cmd': + url = url.replace('&', '^&') + browser += browser_args + [url] if options.kill_on_start: + pname = processname_killed_atexit kill_browser_process() + processname_killed_atexit = pname if options.system_info: logi('Time of run: ' + time.strftime("%x %X")) - logi('Computer name: ' + socket.gethostname()) # http://stackoverflow.com/questions/799767/getting-name-of-windows-computer-running-python-script - logi('OS: ' + get_os_version() + ' with ' + str(get_system_memory()/1024/1024) + ' MB of System RAM') - logi('CPU: ' + get_cpu_infoline()) - print_gpu_infolines() + if options.android: + logi('Model: ' + get_android_model()) + logi('OS: ' + get_android_os_version() + ' with ' + str(get_system_memory()/1024/1024) + ' MB of System RAM') + logi('CPU: ' + get_android_cpu_infoline()) + else: + logi('Computer name: ' + socket.gethostname()) # http://stackoverflow.com/questions/799767/getting-name-of-windows-computer-running-python-script + logi('OS: ' + get_os_version() + ' with ' + str(get_system_memory()/1024/1024) + ' MB of System RAM') + logi('CPU: ' + get_cpu_infoline()) + print_gpu_infolines() if options.browser_info: - logi('Browser: ' + browser_display_name(browser[0]) + ' ' + get_executable_version(browser_exe)) + if options.android: + logi('Browser: Android ' + browser_app) + else: + logi('Browser: ' + browser_display_name(browser[0]) + ' ' + get_executable_version(browser_exe)) # Suppress run warning if requested. if options.no_emrun_detect: emrun_not_enabled_nag_printed = True - global browser_stdout_handle, browser_stderr_handle + global browser_stdout_handle, browser_stderr_handle if options.log_stdout: browser_stdout_handle = open(options.log_stdout, 'ab') if options.log_stderr: @@ -887,6 +1041,10 @@ def main(): else: browser_stderr_handle = open(options.log_stderr, 'ab') + if not options.no_server: + logv('Starting web server in port ' + str(options.port)) + httpd = HTTPWebServer(('', options.port), HTTPHandler) + if not options.no_browser: logv("Executing %s" % ' '.join(browser)) if browser[0] == 'cmd': @@ -894,10 +1052,15 @@ def main(): browser_process = subprocess.Popen(browser) if options.kill_on_exit: atexit.register(kill_browser_process) + # For Android automation, we execute adb, so this process does not represent a browser and no point killing it. + if options.android: + browser_process = None + if browser_process and browser_process.poll() == None: + options.serve_after_close = True + logv('Warning: emrun got detached from the target browser process. Cannot detect when user closes the browser. Behaving as if --serve_after_close was passed in.') + if not options.no_server: - logv('Starting web server in port ' + str(options.port)) - httpd = HTTPWebServer(('', options.port), HTTPHandler) try: httpd.serve_forever() except KeyboardInterrupt: @@ -915,4 +1078,6 @@ def main(): return page_exit_code if __name__ == '__main__': - sys.exit(main()) + returncode = main() + logv('emrun quitting with process exit code ' + str(returncode)) + sys.exit(returncode) |