webruby 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/webruby/environment.rb +4 -0
  3. data/lib/webruby/rake/files.rake +2 -1
  4. data/lib/webruby/rake/mruby.rake +4 -2
  5. data/modules/emscripten/AUTHORS +1 -0
  6. data/modules/emscripten/cmake/Platform/Emscripten.cmake +2 -0
  7. data/modules/emscripten/emcc +96 -40
  8. data/modules/emscripten/emrun +301 -136
  9. data/modules/emscripten/emscripten.py +5 -45
  10. data/modules/emscripten/src/analyzer.js +11 -1
  11. data/modules/emscripten/src/compiler.js +1 -1
  12. data/modules/emscripten/src/emrun_postjs.js +2 -2
  13. data/modules/emscripten/src/emrun_prejs.js +5 -0
  14. data/modules/emscripten/src/emscripten-source-map.min.js +31 -0
  15. data/modules/emscripten/src/library.js +187 -0
  16. data/modules/emscripten/src/library_egl.js +20 -0
  17. data/modules/emscripten/src/library_sdl.js +1 -0
  18. data/modules/emscripten/src/preamble.js +4 -0
  19. data/modules/emscripten/src/relooper/Relooper.cpp +33 -15
  20. data/modules/emscripten/src/relooper/Relooper.h +20 -14
  21. data/modules/emscripten/src/relooper/fuzzer.py +6 -0
  22. data/modules/emscripten/src/relooper/test.cpp +28 -0
  23. data/modules/emscripten/src/relooper/test.txt +211 -166
  24. data/modules/emscripten/src/relooper/test2.txt +20 -20
  25. data/modules/emscripten/src/relooper/test3.txt +41 -41
  26. data/modules/emscripten/src/relooper/test4.txt +26 -26
  27. data/modules/emscripten/src/relooper/test5.txt +52 -52
  28. data/modules/emscripten/src/relooper/test6.txt +19 -19
  29. data/modules/emscripten/src/relooper/test_dead.txt +1 -1
  30. data/modules/emscripten/src/relooper/test_debug.txt +31 -31
  31. data/modules/emscripten/src/relooper/test_fuzz1.txt +50 -50
  32. data/modules/emscripten/src/relooper/test_fuzz2.txt +21 -21
  33. data/modules/emscripten/src/relooper/test_fuzz3.txt +18 -18
  34. data/modules/emscripten/src/relooper/test_fuzz4.txt +28 -28
  35. data/modules/emscripten/src/relooper/test_fuzz5.txt +61 -61
  36. data/modules/emscripten/src/relooper/test_fuzz6.txt +179 -179
  37. data/modules/emscripten/src/relooper/test_inf.txt +846 -846
  38. data/modules/emscripten/src/relooper/testit.sh +15 -15
  39. data/modules/emscripten/system/include/emscripten/emscripten.h +64 -0
  40. data/modules/emscripten/tools/eliminator/asm-eliminator-test-output.js +8 -2
  41. data/modules/emscripten/tools/eliminator/asm-eliminator-test.js +9 -1
  42. data/modules/emscripten/tools/eliminator/eliminator-test-output.js +11 -0
  43. data/modules/emscripten/tools/eliminator/eliminator-test.js +16 -1
  44. data/modules/emscripten/tools/file_packager.py +59 -49
  45. data/modules/emscripten/tools/js-optimizer.js +47 -8
  46. data/modules/emscripten/tools/shared.py +3 -3
  47. data/modules/emscripten/tools/test-js-optimizer-asm-pre-output.js +5 -3
  48. data/modules/emscripten/tools/test-js-optimizer-asm-pre.js +4 -0
  49. data/modules/mruby/INSTALL +11 -6
  50. data/modules/mruby/include/mrbconf.h +0 -3
  51. data/modules/mruby/include/mruby/khash.h +34 -36
  52. data/modules/mruby/include/mruby/string.h +3 -0
  53. data/modules/mruby/include/mruby.h +3 -3
  54. data/modules/mruby/mrblib/string.rb +3 -0
  55. data/modules/mruby/src/class.c +12 -12
  56. data/modules/mruby/src/codegen.c +18 -11
  57. data/modules/mruby/src/hash.c +12 -12
  58. data/modules/mruby/src/kernel.c +3 -3
  59. data/modules/mruby/src/load.c +29 -14
  60. data/modules/mruby/src/numeric.c +1 -1
  61. data/modules/mruby/src/object.c +14 -2
  62. data/modules/mruby/src/state.c +13 -10
  63. data/modules/mruby/src/string.c +1 -3
  64. data/modules/mruby/src/symbol.c +44 -18
  65. data/modules/mruby/src/variable.c +6 -6
  66. data/modules/mruby/test/t/class.rb +34 -0
  67. data/modules/mruby/test/t/module.rb +1 -1
  68. data/modules/mruby/test/t/syntax.rb +28 -0
  69. metadata +5 -13
  70. data/modules/emscripten/src/relooper.js +0 -11516
  71. data/modules/emscripten/src/relooper.js.raw.js +0 -11511
  72. data/modules/emscripten/tools/__init__.pyc +0 -0
  73. data/modules/emscripten/tools/cache.pyc +0 -0
  74. data/modules/emscripten/tools/gen_struct_info.pyc +0 -0
  75. data/modules/emscripten/tools/js_optimizer.pyc +0 -0
  76. data/modules/emscripten/tools/jsrun.pyc +0 -0
  77. data/modules/emscripten/tools/response_file.pyc +0 -0
  78. data/modules/emscripten/tools/shared.pyc +0 -0
  79. data/modules/emscripten/tools/tempfiles.pyc +0 -0
@@ -8,6 +8,7 @@ import os, platform, optparse, logging, re, pprint, atexit, urlparse, subprocess
8
8
  from operator import itemgetter
9
9
  from urllib import unquote
10
10
  from Queue import PriorityQueue
11
+ from threading import Thread, RLock
11
12
 
12
13
  # Populated from cmdline params
13
14
  emrun_options = None
@@ -41,6 +42,9 @@ processname_killed_atexit = ""
41
42
  # If user does not specify a --port parameter, this port is used to launch the server.
42
43
  default_webserver_port = 6931
43
44
 
45
+ # Location of Android Debug Bridge executable
46
+ ADB = ''
47
+
44
48
  # Host OS detection to autolocate browsers and other OS-specific support needs.
45
49
  WINDOWS = False
46
50
  LINUX = False
@@ -73,7 +77,7 @@ last_message_time = time.clock()
73
77
  page_start_time = time.clock()
74
78
 
75
79
  # Stores the time of most recent http page serve.
76
- page_last_served_time = time.clock()
80
+ page_last_served_time = None
77
81
 
78
82
  # Returns given log message formatted to be outputted on a HTML page.
79
83
  def format_html(msg):
@@ -82,21 +86,14 @@ def format_html(msg):
82
86
  msg = cgi.escape(msg)
83
87
  msg = msg.replace('\r\n', '<br />').replace('\n', '<br />')
84
88
  return msg
85
-
89
+
90
+ # HTTP requests are handled from separate threads - synchronize them to avoid race conditions
91
+ http_mutex = RLock()
92
+
86
93
  # Prints a log message to 'info' stdout channel. Always printed.
87
94
  def logi(msg):
88
95
  global last_message_time
89
- if emrun_options.log_html:
90
- sys.stdout.write(format_html(msg))
91
- else:
92
- print >> sys.stdout, msg
93
- sys.stdout.flush()
94
- last_message_time = time.clock()
95
-
96
- # Prints a verbose log message to stdout channel. Only shown if run with --verbose.
97
- def logv(msg):
98
- global emrun_options, last_message_time
99
- if emrun_options.verbose:
96
+ with http_mutex:
100
97
  if emrun_options.log_html:
101
98
  sys.stdout.write(format_html(msg))
102
99
  else:
@@ -104,15 +101,28 @@ def logv(msg):
104
101
  sys.stdout.flush()
105
102
  last_message_time = time.clock()
106
103
 
104
+ # Prints a verbose log message to stdout channel. Only shown if run with --verbose.
105
+ def logv(msg):
106
+ global emrun_options, last_message_time
107
+ with http_mutex:
108
+ if emrun_options.verbose:
109
+ if emrun_options.log_html:
110
+ sys.stdout.write(format_html(msg))
111
+ else:
112
+ print >> sys.stdout, msg
113
+ sys.stdout.flush()
114
+ last_message_time = time.clock()
115
+
107
116
  # Prints an error message to stderr channel.
108
117
  def loge(msg):
109
118
  global last_message_time
110
- if emrun_options.log_html:
111
- sys.stderr.write(format_html(msg))
112
- else:
113
- print >> sys.stderr, msg
114
- sys.stderr.flush()
115
- last_message_time = time.clock()
119
+ with http_mutex:
120
+ if emrun_options.log_html:
121
+ sys.stderr.write(format_html(msg))
122
+ else:
123
+ print >> sys.stderr, msg
124
+ sys.stderr.flush()
125
+ last_message_time = time.clock()
116
126
 
117
127
  def format_eol(msg):
118
128
  if WINDOWS:
@@ -122,8 +132,6 @@ def format_eol(msg):
122
132
  # Prints a message to the browser stdout output stream.
123
133
  def browser_logi(msg):
124
134
  global browser_stdout_handle
125
- if browser_stdout_handle != sys.stdout and not msg.endswith('\n'):
126
- msg += '\n'
127
135
  msg = format_eol(msg)
128
136
  print >> browser_stdout_handle, msg
129
137
  browser_stdout_handle.flush()
@@ -132,8 +140,6 @@ def browser_logi(msg):
132
140
  # Prints a message to the browser stderr output stream.
133
141
  def browser_loge(msg):
134
142
  global browser_stderr_handle
135
- if browser_stderr_handle != sys.stderr and not msg.endswith('\n'):
136
- msg += '\n'
137
143
  msg = format_eol(msg)
138
144
  print >> browser_stderr_handle, msg
139
145
  browser_stderr_handle.flush()
@@ -153,7 +159,7 @@ def is_browser_process_alive():
153
159
 
154
160
  # Kills browser_process and processname_killed_atexit.
155
161
  def kill_browser_process():
156
- global browser_process, processname_killed_atexit
162
+ global browser_process, processname_killed_atexit, emrun_options, ADB
157
163
  if browser_process:
158
164
  try:
159
165
  logv('Terminating browser process..')
@@ -161,21 +167,25 @@ def kill_browser_process():
161
167
  except:
162
168
  pass
163
169
  browser_process = None
164
-
165
170
  if len(processname_killed_atexit) > 0:
166
- logv("Terminating all processes that have string '" + processname_killed_atexit + "' in their name.")
167
- if WINDOWS:
168
- process_image = processname_killed_atexit if '.exe' in processname_killed_atexit else (processname_killed_atexit + '.exe')
169
- process = subprocess.Popen(['taskkill', '/F', '/IM', process_image, '/T'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
170
- process.communicate()
171
+ if emrun_options.android:
172
+ logv("Terminating Android app '" + processname_killed_atexit + "'.")
173
+ subprocess.call([ADB, 'shell', 'am', 'force-stop', processname_killed_atexit])
171
174
  else:
172
- try:
173
- subprocess.call(['pkill', processname_killed_atexit])
174
- except OSError, e:
175
+ logv("Terminating all processes that have string '" + processname_killed_atexit + "' in their name.")
176
+ if WINDOWS:
177
+ process_image = processname_killed_atexit if '.exe' in processname_killed_atexit else (processname_killed_atexit + '.exe')
178
+ process = subprocess.Popen(['taskkill', '/F', '/IM', process_image, '/T'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
179
+ process.communicate()
180
+ else:
175
181
  try:
176
- subprocess.call(['killall', processname_killed_atexit])
182
+ subprocess.call(['pkill', processname_killed_atexit])
177
183
  except OSError, e:
178
- loge('Both commands pkill and killall failed to clean up the spawned browser process. Perhaps neither of these utilities is available on your system?')
184
+ try:
185
+ subprocess.call(['killall', processname_killed_atexit])
186
+ except OSError, e:
187
+ loge('Both commands pkill and killall failed to clean up the spawned browser process. Perhaps neither of these utilities is available on your system?')
188
+ # Clear the process name to represent that the browser is now dead.
179
189
  processname_killed_atexit = ''
180
190
 
181
191
  # 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):
190
200
 
191
201
  def handle_incoming_message(self, seq_num, log, data):
192
202
  global have_received_messages
193
- have_received_messages = True
194
-
195
- if self.expected_http_seq_num == -1:
196
- self.expected_http_seq_num = seq_num+1
197
- log(data)
198
- elif seq_num == -1: # Message arrived without a sequence number? Just log immediately
199
- log(data)
200
- elif seq_num == self.expected_http_seq_num:
201
- log(data)
202
- self.expected_http_seq_num += 1
203
- self.print_messages_due()
204
- elif seq_num < self.expected_http_seq_num:
205
- log(data)
206
- else:
207
- log(data)
208
- self.http_message_queue += [(seq_num, data, log)]
209
- self.http_message_queue.sort(key=itemgetter(0))
210
- if len(self.http_message_queue) > 16:
211
- self.print_next_message()
203
+ with http_mutex:
204
+ have_received_messages = True
205
+
206
+ if self.expected_http_seq_num == -1:
207
+ self.expected_http_seq_num = seq_num+1
208
+ log(data)
209
+ elif seq_num == -1: # Message arrived without a sequence number? Just log immediately
210
+ log(data)
211
+ elif seq_num == self.expected_http_seq_num:
212
+ log(data)
213
+ self.expected_http_seq_num += 1
214
+ self.print_messages_due()
215
+ elif seq_num < self.expected_http_seq_num:
216
+ log(data)
217
+ else:
218
+ self.http_message_queue += [(seq_num, data, log)]
219
+ self.http_message_queue.sort(key=itemgetter(0))
220
+ if len(self.http_message_queue) > 16:
221
+ self.print_next_message()
212
222
 
213
223
  # If it's been too long since we we got a message, prints out the oldest queued message, ignoring the proper order.
214
224
  # This ensures that if any messages are actually lost, that the message queue will be orderly flushed.
215
225
  def print_timed_out_messages(self):
216
226
  global last_message_time
217
- now = time.clock()
218
- max_message_queue_time = 5
219
- if len(self.http_message_queue) > 0 and now - last_message_time > max_message_queue_time:
220
- self.print_next_message()
227
+ with http_mutex:
228
+ now = time.clock()
229
+ max_message_queue_time = 5
230
+ if len(self.http_message_queue) > 0 and now - last_message_time > max_message_queue_time:
231
+ self.print_next_message()
221
232
 
222
233
  # Skips to printing the next message in queue now, independent of whether there was missed messages in the sequence numbering.
223
234
  def print_next_message(self):
224
- if len(self.http_message_queue) > 0:
225
- self.expected_http_seq_num = self.http_message_queue[0][0]
226
- self.print_messages_due()
235
+ with http_mutex:
236
+ if len(self.http_message_queue) > 0:
237
+ self.expected_http_seq_num = self.http_message_queue[0][0]
238
+ self.print_messages_due()
227
239
 
228
240
  # Completely flushes all out-of-order messages in the queue.
229
241
  def print_all_messages(self):
230
- while len(self.http_message_queue) > 0:
231
- self.print_next_message()
242
+ with http_mutex:
243
+ while len(self.http_message_queue) > 0:
244
+ self.print_next_message()
232
245
 
233
246
  # Prints any messages that are now due after we logged some other previous messages.
234
247
  def print_messages_due(self):
235
- while len(self.http_message_queue) > 0:
236
- msg = self.http_message_queue[0]
237
- if msg[0] == self.expected_http_seq_num:
238
- msg[2](msg[1])
239
- self.expected_http_seq_num += 1
240
- self.http_message_queue.pop(0)
241
- else:
242
- return
248
+ with http_mutex:
249
+ while len(self.http_message_queue) > 0:
250
+ msg = self.http_message_queue[0]
251
+ if msg[0] == self.expected_http_seq_num:
252
+ msg[2](msg[1])
253
+ self.expected_http_seq_num += 1
254
+ self.http_message_queue.pop(0)
255
+ else:
256
+ return
243
257
 
244
258
  def serve_forever(self, timeout=0.5):
245
259
  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):
278
292
  page_exit_code = emrun_options.timeout_returncode
279
293
 
280
294
  # If we detect that the page is not running with emrun enabled, print a warning message.
281
- time_since_page_serve = now - page_last_served_time
282
- if not emrun_not_enabled_nag_printed and not have_received_messages and time_since_page_serve > 5:
283
- 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.')
284
- emrun_not_enabled_nag_printed = True
295
+ if not emrun_not_enabled_nag_printed and page_last_served_time is not None:
296
+ time_since_page_serve = now - page_last_served_time
297
+ if not have_received_messages and time_since_page_serve > 10:
298
+ 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.')
299
+ emrun_not_enabled_nag_printed = True
285
300
 
286
301
  # Clean up at quit, print any leftover messages in queue.
287
302
  self.print_all_messages()
@@ -411,6 +426,19 @@ def get_cpu_infoline():
411
426
 
412
427
  return platform.machine() + ', ' + cpu_name
413
428
 
429
+ def get_android_cpu_infoline():
430
+ lines = subprocess.check_output([ADB, 'shell', 'cat', '/proc/cpuinfo']).split('\n')
431
+ processor = ''
432
+ hardware = ''
433
+ for line in lines:
434
+ if line.startswith('Processor'):
435
+ processor = line[line.find(':')+1:].strip()
436
+ elif line.startswith('Hardware'):
437
+ hardware = line[line.find(':')+1:].strip()
438
+
439
+ freq = int(subprocess.check_output([ADB, 'shell', 'cat', '/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq']).strip())/1000
440
+ return 'CPU: ' + processor + ', ' + hardware + ' @ ' + str(freq) + ' MHz'
441
+
414
442
  def win_print_gpu_info():
415
443
  gpus = []
416
444
  gpu_memory = []
@@ -575,19 +603,24 @@ def get_os_version():
575
603
  return 'Unknown OS'
576
604
 
577
605
  def get_system_memory():
606
+ global emrun_options
607
+
578
608
  try:
579
- if WINDOWS:
580
- return win32api.GlobalMemoryStatusEx()['TotalPhys']
581
- elif OSX:
582
- return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']).strip())
583
- elif LINUX:
584
- mem = open('/proc/meminfo', 'r')
585
- lines = mem.readlines()
586
- mem.close()
609
+ if LINUX or emrun_options.android:
610
+ if emrun_options.android:
611
+ lines = subprocess.check_output([ADB, 'shell', 'cat', '/proc/meminfo']).split('\n')
612
+ else:
613
+ mem = open('/proc/meminfo', 'r')
614
+ lines = mem.readlines()
615
+ mem.close()
587
616
  for i in lines:
588
617
  sline = i.split()
589
618
  if str(sline[0]) == 'MemTotal:':
590
619
  return int(sline[1]) * 1024
620
+ elif WINDOWS:
621
+ return win32api.GlobalMemoryStatusEx()['TotalPhys']
622
+ elif OSX:
623
+ return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']).strip())
591
624
  except:
592
625
  return -1
593
626
 
@@ -698,6 +731,75 @@ def find_browser(name):
698
731
 
699
732
  return None # Could not find the browser
700
733
 
734
+ def get_android_model():
735
+ global ADB
736
+ manufacturer = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.manufacturer']).strip()
737
+ brand = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.brand']).strip()
738
+ model = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.model']).strip()
739
+ board = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.board']).strip()
740
+ device = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.device']).strip()
741
+ name = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.product.name']).strip()
742
+ return manufacturer + ' ' + brand + ' ' + model + ' ' + board + ' ' + device + ' ' + name
743
+
744
+ def get_android_os_version():
745
+ global ADB
746
+ ver = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.release']).strip()
747
+ apiLevel = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.sdk']).strip()
748
+ if not apiLevel:
749
+ apiLevel = subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.version.sdk_int']).strip()
750
+
751
+ os = ''
752
+ if ver:
753
+ os += 'Android ' + ver + ' '
754
+ if apiLevel:
755
+ os += 'SDK API Level ' + apiLevel + ' '
756
+ os += subprocess.check_output([ADB, 'shell', 'getprop', 'ro.build.description']).strip()
757
+ return os
758
+
759
+ def list_android_browsers():
760
+ global ADB
761
+ apps = subprocess.check_output([ADB, 'shell', 'pm', 'list', 'packages', '-f']).replace('\r\n', '\n')
762
+ browsers = []
763
+ for line in apps.split('\n'):
764
+ line = line.strip()
765
+ if line.endswith('=org.mozilla.firefox'):
766
+ browsers += ['firefox']
767
+ if line.endswith('=org.mozilla.firefox_beta'):
768
+ browsers += ['firefox_beta']
769
+ if line.endswith('=org.mozilla.fennec_aurora'):
770
+ browsers += ['firefox_aurora']
771
+ if line.endswith('=org.mozilla.fennec'):
772
+ browsers += ['firefox_nightly']
773
+ if line.endswith('=com.android.chrome'):
774
+ browsers += ['chrome']
775
+ if line.endswith('=com.chrome.beta'):
776
+ browsers += ['chrome_beta']
777
+ if line.endswith('=com.opera.browser'):
778
+ browsers += ['opera']
779
+ if line.endswith('=com.opera.mini.android'):
780
+ browsers += ['opera_mini']
781
+ if line.endswith('=mobi.mgeek.TunnyBrowser'):
782
+ browsers += ['dolphin']
783
+
784
+ browsers.sort()
785
+ logi('emrun has automatically found the following browsers on the connected Android device:')
786
+ for browser in browsers:
787
+ logi(' - ' + browser)
788
+
789
+ def list_pc_browsers():
790
+ browsers = ['firefox', 'firefox_beta', 'firefox_aurora', 'firefox_nightly', 'chrome', 'chrome_canary', 'iexplore', 'safari', 'opera']
791
+ logi('emrun has automatically found the following browsers in the default install locations on the system:')
792
+ logi('')
793
+ for browser in browsers:
794
+ browser_exe = find_browser(browser)
795
+ if type(browser_exe) == list:
796
+ browser_exe = browser_exe[0]
797
+ if browser_exe:
798
+ logi(' - ' + browser + ': ' + browser_display_name(browser_exe) + ' ' + get_executable_version(browser_exe))
799
+ logi('')
800
+ logi('You can pass the --browser <id> option to launch with the given browser above.')
801
+ logi('Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.')
802
+
701
803
  def browser_display_name(browser):
702
804
  b = browser.lower()
703
805
  if 'iexplore' in b:
@@ -718,7 +820,7 @@ def browser_display_name(browser):
718
820
  return browser
719
821
 
720
822
  def main():
721
- global browser_process, processname_killed_atexit, emrun_options, emrun_not_enabled_nag_printed
823
+ global browser_process, processname_killed_atexit, emrun_options, emrun_not_enabled_nag_printed, ADB
722
824
  usage_str = "usage: %prog [options] [optional_portnum]"
723
825
  parser = optparse.OptionParser(usage=usage_str)
724
826
 
@@ -773,6 +875,9 @@ def main():
773
875
  parser.add_option('--browser', dest='browser', default='',
774
876
  help='Specifies the browser executable to run the web page in.')
775
877
 
878
+ parser.add_option('--android', dest='android', action='store_true', default=False,
879
+ help='Launches the page in a browser of an Android device connected to an USB on the local system. (via adb)')
880
+
776
881
  parser.add_option('--system_info', dest='system_info', action='store_true',
777
882
  help='Prints information about the current system at startup.')
778
883
 
@@ -785,7 +890,13 @@ def main():
785
890
  (options, args) = parser.parse_args(sys.argv)
786
891
  emrun_options = options
787
892
 
788
- if not options.browser:
893
+ if options.android:
894
+ ADB = which('adb')
895
+ if not ADB:
896
+ loge("Could not find the adb tool. Install Android SDK and add the directory of adb to PATH.")
897
+ return 1
898
+
899
+ if not options.browser and not options.android:
789
900
  if WINDOWS:
790
901
  options.browser = 'start'
791
902
  elif LINUX:
@@ -795,19 +906,11 @@ def main():
795
906
  elif OSX:
796
907
  options.browser = 'safari'
797
908
 
798
- browsers = ['firefox', 'firefox_beta', 'firefox_aurora', 'firefox_nightly', 'chrome', 'chrome_canary', 'iexplore', 'safari', 'opera']
799
909
  if options.list_browsers:
800
- logi('emrun has automatically found the following browsers in the default install locations on the system:')
801
- logi('')
802
- for browser in browsers:
803
- browser_exe = find_browser(browser)
804
- if type(browser_exe) == list:
805
- browser_exe = browser_exe[0]
806
- if browser_exe:
807
- logi(' - ' + browser + ': ' + browser_display_name(browser_exe) + ' ' + get_executable_version(browser_exe))
808
- logi('')
809
- logi('You can pass the --browser <id> option to launch with the given browser above.')
810
- logi('Even if your browser was not detected, you can use --browser /path/to/browser/executable to launch with that browser.')
910
+ if options.android:
911
+ list_android_browsers()
912
+ else:
913
+ list_pc_browsers()
811
914
  return
812
915
 
813
916
  if len(args) < 2 and (options.system_info or options.browser_info):
@@ -827,58 +930,109 @@ def main():
827
930
  url = os.path.relpath(os.path.abspath(file_to_serve), serve_dir)
828
931
  if len(cmdlineparams) > 0:
829
932
  url += '?' + '&'.join(cmdlineparams)
830
- url = 'http://localhost:'+str(options.port)+'/'+url
933
+ server_root = 'localhost'
934
+ if options.android:
935
+ server_root = socket.gethostbyname(socket.gethostname())
936
+ url = 'http://' + server_root + ':' + str(options.port)+'/'+url
831
937
 
832
938
  os.chdir(serve_dir)
833
939
  logv('Web server root directory: ' + os.path.abspath('.'))
834
940
 
835
- browser = find_browser(str(options.browser))
836
- browser_exe = browser[0]
837
- browser_args = []
838
-
839
- if 'safari' in browser_exe.lower():
840
- # Safari has a bug that a command line 'Safari http://page.com' does not launch that page,
841
- # but instead launches 'file:///http://page.com'. To remedy this, must use the open -a command
842
- # to run Safari, but unfortunately this will end up spawning Safari process detached from emrun.
843
- if OSX:
844
- browser = ['open', '-a', 'Safari'] + (browser[1:] if len(browser) > 1 else [])
845
-
846
- processname_killed_atexit = 'Safari'
847
- elif 'chrome' in browser_exe.lower():
848
- processname_killed_atexit = 'chrome'
849
- 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']
850
- # if options.no_server:
851
- # browser_args += ['--disable-web-security']
852
- elif 'firefox' in browser_exe.lower():
853
- processname_killed_atexit = 'firefox'
854
- elif 'iexplore' in browser_exe.lower():
855
- processname_killed_atexit = 'iexplore'
856
- browser_args = ['-private']
857
- elif 'opera' in browser_exe.lower():
858
- processname_killed_atexit = 'opera'
859
-
860
- # In Windows cmdline, & character delimits multiple commmands, so must use ^ to escape them.
861
- if browser_exe == 'cmd':
862
- url = url.replace('&', '^&')
863
- browser += browser_args + [url]
941
+ if options.android:
942
+ if not options.no_browser:
943
+ if not options.browser:
944
+ 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.")
945
+ return 1
946
+ elif options.browser == 'firefox':
947
+ browser_app = 'org.mozilla.firefox/.App'
948
+ elif options.browser == 'firefox_beta':
949
+ browser_app = 'org.mozilla.firefox_beta/.App'
950
+ elif options.browser == 'firefox_aurora' or options.browser == 'fennec_aurora':
951
+ browser_app = 'org.mozilla.fennec_aurora/.App'
952
+ elif options.browser == 'firefox_nightly' or options.browser == 'fennec':
953
+ browser_app = 'org.mozilla.fennec/.App'
954
+ elif options.browser == 'chrome':
955
+ browser_app = 'com.android.chrome/.Main'
956
+ elif options.browser == 'chrome_beta' or options.browser == 'chrome_canary': # There is no Chrome Canary for Android, but Play store has 'Chrome Beta' instead.
957
+ browser_app = 'com.chrome.beta/com.android.chrome.Main'
958
+ elif options.browser == 'opera':
959
+ browser_app = 'com.opera.browser/com.opera.Opera'
960
+ elif options.browser == 'opera_mini': # Launching the URL works, but page seems to never load (Fails with 'Network problem' even when other browsers work)
961
+ browser_app = 'com.opera.mini.android/.Browser'
962
+ elif options.browser =='dolphin': # Current stable Dolphin as of 12/2013 does not have WebGL support.
963
+ browser_app = 'mobi.mgeek.TunnyBrowser/.BrowserActivity'
964
+ else:
965
+ loge("Don't know how to launch browser " + options.browser + ' on Android!')
966
+ return 1
967
+ # To add support for a new Android browser in the list above:
968
+ # 1. Install the browser to Android phone, connect it via adb to PC.
969
+ # 2. Type 'adb shell pm list packages -f' to locate the package name of that application.
970
+ # 3. Type 'adb pull <packagename>.apk' to copy the apk of that application to PC.
971
+ # 4. Type 'aapt d xmltree <packagename>.apk AndroidManifest.xml > manifest.txt' to extract the manifest from the package.
972
+ # 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'
973
+
974
+ if WINDOWS:
975
+ url = url.replace('&', '\\&')
976
+ browser = [ADB, 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-n', browser_app, '-d', url]
977
+ processname_killed_atexit = browser_app[:browser_app.find('/')]
978
+ else: #Launching a web page on local system.
979
+ browser = find_browser(str(options.browser))
980
+ browser_exe = browser[0]
981
+ browser_args = []
982
+
983
+ if 'safari' in browser_exe.lower():
984
+ # Safari has a bug that a command line 'Safari http://page.com' does not launch that page,
985
+ # but instead launches 'file:///http://page.com'. To remedy this, must use the open -a command
986
+ # to run Safari, but unfortunately this will end up spawning Safari process detached from emrun.
987
+ if OSX:
988
+ browser = ['open', '-a', 'Safari'] + (browser[1:] if len(browser) > 1 else [])
989
+
990
+ processname_killed_atexit = 'Safari'
991
+ elif 'chrome' in browser_exe.lower():
992
+ processname_killed_atexit = 'chrome'
993
+ 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']
994
+ # if options.no_server:
995
+ # browser_args += ['--disable-web-security']
996
+ elif 'firefox' in browser_exe.lower():
997
+ processname_killed_atexit = 'firefox'
998
+ elif 'iexplore' in browser_exe.lower():
999
+ processname_killed_atexit = 'iexplore'
1000
+ browser_args = ['-private']
1001
+ elif 'opera' in browser_exe.lower():
1002
+ processname_killed_atexit = 'opera'
1003
+
1004
+ # In Windows cmdline, & character delimits multiple commmands, so must use ^ to escape them.
1005
+ if browser_exe == 'cmd':
1006
+ url = url.replace('&', '^&')
1007
+ browser += browser_args + [url]
864
1008
 
865
1009
  if options.kill_on_start:
1010
+ pname = processname_killed_atexit
866
1011
  kill_browser_process()
1012
+ processname_killed_atexit = pname
867
1013
 
868
1014
  if options.system_info:
869
1015
  logi('Time of run: ' + time.strftime("%x %X"))
870
- logi('Computer name: ' + socket.gethostname()) # http://stackoverflow.com/questions/799767/getting-name-of-windows-computer-running-python-script
871
- logi('OS: ' + get_os_version() + ' with ' + str(get_system_memory()/1024/1024) + ' MB of System RAM')
872
- logi('CPU: ' + get_cpu_infoline())
873
- print_gpu_infolines()
1016
+ if options.android:
1017
+ logi('Model: ' + get_android_model())
1018
+ logi('OS: ' + get_android_os_version() + ' with ' + str(get_system_memory()/1024/1024) + ' MB of System RAM')
1019
+ logi('CPU: ' + get_android_cpu_infoline())
1020
+ else:
1021
+ logi('Computer name: ' + socket.gethostname()) # http://stackoverflow.com/questions/799767/getting-name-of-windows-computer-running-python-script
1022
+ logi('OS: ' + get_os_version() + ' with ' + str(get_system_memory()/1024/1024) + ' MB of System RAM')
1023
+ logi('CPU: ' + get_cpu_infoline())
1024
+ print_gpu_infolines()
874
1025
  if options.browser_info:
875
- logi('Browser: ' + browser_display_name(browser[0]) + ' ' + get_executable_version(browser_exe))
1026
+ if options.android:
1027
+ logi('Browser: Android ' + browser_app)
1028
+ else:
1029
+ logi('Browser: ' + browser_display_name(browser[0]) + ' ' + get_executable_version(browser_exe))
876
1030
 
877
1031
  # Suppress run warning if requested.
878
1032
  if options.no_emrun_detect:
879
1033
  emrun_not_enabled_nag_printed = True
880
1034
 
881
- global browser_stdout_handle, browser_stderr_handle
1035
+ global browser_stdout_handle, browser_stderr_handle
882
1036
  if options.log_stdout:
883
1037
  browser_stdout_handle = open(options.log_stdout, 'ab')
884
1038
  if options.log_stderr:
@@ -887,6 +1041,10 @@ def main():
887
1041
  else:
888
1042
  browser_stderr_handle = open(options.log_stderr, 'ab')
889
1043
 
1044
+ if not options.no_server:
1045
+ logv('Starting web server in port ' + str(options.port))
1046
+ httpd = HTTPWebServer(('', options.port), HTTPHandler)
1047
+
890
1048
  if not options.no_browser:
891
1049
  logv("Executing %s" % ' '.join(browser))
892
1050
  if browser[0] == 'cmd':
@@ -894,10 +1052,15 @@ def main():
894
1052
  browser_process = subprocess.Popen(browser)
895
1053
  if options.kill_on_exit:
896
1054
  atexit.register(kill_browser_process)
1055
+ # For Android automation, we execute adb, so this process does not represent a browser and no point killing it.
1056
+ if options.android:
1057
+ browser_process = None
897
1058
 
1059
+ if browser_process and browser_process.poll() == None:
1060
+ options.serve_after_close = True
1061
+ 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.')
1062
+
898
1063
  if not options.no_server:
899
- logv('Starting web server in port ' + str(options.port))
900
- httpd = HTTPWebServer(('', options.port), HTTPHandler)
901
1064
  try:
902
1065
  httpd.serve_forever()
903
1066
  except KeyboardInterrupt:
@@ -915,4 +1078,6 @@ def main():
915
1078
  return page_exit_code
916
1079
 
917
1080
  if __name__ == '__main__':
918
- sys.exit(main())
1081
+ returncode = main()
1082
+ logv('emrun quitting with process exit code ' + str(returncode))
1083
+ sys.exit(returncode)