| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2011 The Native Client Authors. All rights reserved. | 2 # Copyright (c) 2011 The Native Client Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import os.path | 6 import os.path |
| 7 import re | 7 import re |
| 8 import shutil | 8 import shutil |
| 9 import subprocess |
| 9 import sys | 10 import sys |
| 10 import tempfile | 11 import tempfile |
| 11 import time | 12 import time |
| 12 | 13 |
| 13 import browserprocess | 14 # mozrunner is needed as long as we are supporting versions of Python |
| 14 | 15 # before 2.6. |
| 15 class LaunchFailure(Exception): | 16 import mozrunner |
| 16 pass | |
| 17 | |
| 18 | |
| 19 def GetPlatform(): | |
| 20 if sys.platform == 'darwin': | |
| 21 platform = 'mac' | |
| 22 elif sys.platform.startswith('linux'): | |
| 23 platform = 'linux' | |
| 24 elif sys.platform in ('cygwin', 'win32'): | |
| 25 platform = 'windows' | |
| 26 else: | |
| 27 raise LaunchFailure('Unknown platform: %s' % sys.platform) | |
| 28 return platform | |
| 29 | |
| 30 | |
| 31 PLATFORM = GetPlatform() | |
| 32 | 17 |
| 33 | 18 |
| 34 def SelectRunCommand(): | 19 def SelectRunCommand(): |
| 35 # The subprocess module added support for .kill in Python 2.6 | 20 # The subprocess module added support for .kill in Python 2.6 |
| 36 if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and | 21 if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and |
| 37 sys.version_info[1] < 6): | 22 sys.version_info[1] < 6): |
| 38 return browserprocess.RunCommandWithMozrunner | 23 return mozrunner.run_command |
| 39 elif PLATFORM == 'linux': | |
| 40 return browserprocess.RunCommandInProcessGroup | |
| 41 else: | 24 else: |
| 42 return browserprocess.RunCommandWithSubprocess | 25 return subprocess.Popen |
| 43 | 26 |
| 44 | 27 |
| 45 RunCommand = SelectRunCommand() | 28 RunCommand = SelectRunCommand() |
| 46 | 29 |
| 47 def RemoveDirectory(path): | 30 def RemoveDirectory(path): |
| 48 retry = 5 | 31 retry = 5 |
| 49 sleep_time = 0.25 | 32 sleep_time = 0.25 |
| 50 while True: | 33 while True: |
| 51 try: | 34 try: |
| 52 shutil.rmtree(path) | 35 shutil.rmtree(path) |
| 53 except Exception: | 36 except Exception: |
| 54 # Windows processes sometime hang onto files too long | 37 # Windows processes sometime hang onto files too long |
| 55 if retry > 0: | 38 if retry > 0: |
| 56 retry -= 1 | 39 retry -= 1 |
| 57 time.sleep(sleep_time) | 40 time.sleep(sleep_time) |
| 58 sleep_time *= 2 | 41 sleep_time *= 2 |
| 59 else: | 42 else: |
| 60 # No luck - don't mask the error | 43 # No luck - don't mask the error |
| 61 raise | 44 raise |
| 62 else: | 45 else: |
| 63 # succeeded | 46 # succeeded |
| 64 break | 47 break |
| 65 | 48 |
| 66 | 49 |
| 50 class LaunchFailure(Exception): |
| 51 pass |
| 52 |
| 53 |
| 54 def GetPlatform(): |
| 55 if sys.platform == 'darwin': |
| 56 platform = 'mac' |
| 57 elif sys.platform.startswith('linux'): |
| 58 platform = 'linux' |
| 59 elif sys.platform in ('cygwin', 'win32'): |
| 60 platform = 'windows' |
| 61 else: |
| 62 raise LaunchFailure('Unknown platform: %s' % sys.platform) |
| 63 return platform |
| 64 |
| 65 |
| 66 PLATFORM = GetPlatform() |
| 67 |
| 67 | 68 |
| 68 # In Windows, subprocess seems to have an issue with file names that | 69 # In Windows, subprocess seems to have an issue with file names that |
| 69 # contain spaces. | 70 # contain spaces. |
| 70 def EscapeSpaces(path): | 71 def EscapeSpaces(path): |
| 71 if PLATFORM == 'windows' and ' ' in path: | 72 if PLATFORM == 'windows' and ' ' in path: |
| 72 return '"%s"' % path | 73 return '"%s"' % path |
| 73 return path | 74 return path |
| 74 | 75 |
| 75 | 76 |
| 76 def MakeEnv(options): | 77 def MakeEnv(options): |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 return self.options.browser_path | 119 return self.options.browser_path |
| 119 else: | 120 else: |
| 120 path = self.KnownPath() | 121 path = self.KnownPath() |
| 121 if path is None or not os.path.exists(path): | 122 if path is None or not os.path.exists(path): |
| 122 raise LaunchFailure('Cannot find the browser directory') | 123 raise LaunchFailure('Cannot find the browser directory') |
| 123 binary = os.path.join(path, self.BinaryName()) | 124 binary = os.path.join(path, self.BinaryName()) |
| 124 if not os.path.exists(binary): | 125 if not os.path.exists(binary): |
| 125 raise LaunchFailure('Cannot find the browser binary') | 126 raise LaunchFailure('Cannot find the browser binary') |
| 126 return binary | 127 return binary |
| 127 | 128 |
| 129 # subprocess.wait() doesn't have a timeout, unfortunately. |
| 128 def WaitForProcessDeath(self): | 130 def WaitForProcessDeath(self): |
| 129 self.browser_process.Wait(self.WAIT_STEPS, self.SLEEP_TIME) | 131 i = 0 |
| 132 while self.handle.poll() is None and i < self.WAIT_STEPS: |
| 133 time.sleep(self.SLEEP_TIME) |
| 134 i += 1 |
| 130 | 135 |
| 131 def Cleanup(self): | 136 def Cleanup(self): |
| 132 self.browser_process.Kill() | 137 if self.handle.poll() is None: |
| 138 print 'KILLING the browser' |
| 139 try: |
| 140 self.handle.kill() |
| 141 # If it doesn't die, we hang. Oh well. |
| 142 self.handle.wait() |
| 143 except Exception: |
| 144 # If it is already dead, then it's ok. |
| 145 # This may happen if the browser dies after the first poll, but before |
| 146 # the kill. |
| 147 if self.handle.poll() is None: |
| 148 raise |
| 133 | 149 |
| 134 RemoveDirectory(self.profile) | 150 RemoveDirectory(self.profile) |
| 135 if self.tool_log_dir is not None: | 151 if self.tool_log_dir is not None: |
| 136 RemoveDirectory(self.tool_log_dir) | 152 RemoveDirectory(self.tool_log_dir) |
| 137 | 153 |
| 138 def MakeProfileDirectory(self): | 154 def MakeProfileDirectory(self): |
| 139 self.profile = tempfile.mkdtemp(prefix='browserprofile_') | 155 self.profile = tempfile.mkdtemp(prefix='browserprofile_') |
| 140 return self.profile | 156 return self.profile |
| 141 | 157 |
| 142 def Launch(self, cmd, env): | 158 def Launch(self, cmd, env): |
| 143 browser_path = cmd[0] | 159 browser_path = cmd[0] |
| 144 if not os.path.exists(browser_path): | 160 if not os.path.exists(browser_path): |
| 145 raise LaunchFailure('Browser does not exist %r'% browser_path) | 161 raise LaunchFailure('Browser does not exist %r'% browser_path) |
| 146 if not os.access(browser_path, os.X_OK): | 162 if not os.access(browser_path, os.X_OK): |
| 147 raise LaunchFailure('Browser cannot be executed %r (Is this binary on an ' | 163 raise LaunchFailure('Browser cannot be executed %r (Is this binary on an ' |
| 148 'NFS volume?)' % browser_path) | 164 'NFS volume?)' % browser_path) |
| 149 if self.options.sel_ldr: | 165 if self.options.sel_ldr: |
| 150 env['NACL_SEL_LDR'] = self.options.sel_ldr | 166 env['NACL_SEL_LDR'] = self.options.sel_ldr |
| 151 if self.options.irt_library: | 167 if self.options.irt_library: |
| 152 env['NACL_IRT_LIBRARY'] = self.options.irt_library | 168 env['NACL_IRT_LIBRARY'] = self.options.irt_library |
| 153 if self.options.enable_experimental_js: | 169 if self.options.enable_experimental_js: |
| 154 env['NACL_ENABLE_EXPERIMENTAL_JAVASCRIPT_APIS'] = '1' | 170 env['NACL_ENABLE_EXPERIMENTAL_JAVASCRIPT_APIS'] = '1' |
| 155 if self.options.prefer_portable_in_manifest: | 171 if self.options.prefer_portable_in_manifest: |
| 156 env['NACL_PREFER_PORTABLE_IN_MANIFEST'] = '1' | 172 env['NACL_PREFER_PORTABLE_IN_MANIFEST'] = '1' |
| 157 print 'ENV:', ' '.join(['='.join(pair) for pair in env.iteritems()]) | 173 print 'ENV:', ' '.join(['='.join(pair) for pair in env.iteritems()]) |
| 158 print 'LAUNCHING: %s' % ' '.join(cmd) | 174 print 'LAUNCHING: %s' % ' '.join(cmd) |
| 159 sys.stdout.flush() | 175 sys.stdout.flush() |
| 160 self.browser_process = RunCommand(cmd, env=env) | 176 self.handle = RunCommand(cmd, env=env) |
| 177 print 'PID', self.handle.pid |
| 161 | 178 |
| 162 def IsRunning(self): | 179 def IsRunning(self): |
| 163 return self.browser_process.IsRunning() | 180 return self.handle.poll() is None |
| 164 | 181 |
| 165 def GetReturnCode(self): | 182 def GetReturnCode(self): |
| 166 return self.browser_process.GetReturnCode() | 183 return self.handle.returncode |
| 167 | 184 |
| 168 def Run(self, url, port): | 185 def Run(self, url, port): |
| 169 self.binary = EscapeSpaces(self.FindBinary()) | 186 self.binary = EscapeSpaces(self.FindBinary()) |
| 170 self.profile = self.CreateProfile() | 187 self.profile = self.CreateProfile() |
| 171 if self.options.tool is not None: | 188 if self.options.tool is not None: |
| 172 self.tool_log_dir = self.CreateToolLogDir() | 189 self.tool_log_dir = self.CreateToolLogDir() |
| 173 cmd = self.MakeCmd(url, port) | 190 cmd = self.MakeCmd(url, port) |
| 174 self.Launch(cmd, MakeEnv(self.options)) | 191 self.Launch(cmd, MakeEnv(self.options)) |
| 175 | 192 |
| 176 | 193 |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 272 '--trace-children=yes', | 289 '--trace-children=yes', |
| 273 '--nacl-file=%s' % (self.options.files[0],), | 290 '--nacl-file=%s' % (self.options.files[0],), |
| 274 '--ignore=../tools/valgrind/tsan/ignores.txt', | 291 '--ignore=../tools/valgrind/tsan/ignores.txt', |
| 275 '--suppressions=../tools/valgrind/tsan/suppressions.txt', | 292 '--suppressions=../tools/valgrind/tsan/suppressions.txt', |
| 276 '--log-file=%s/log.%%p' % (self.tool_log_dir,)] + cmd | 293 '--log-file=%s/log.%%p' % (self.tool_log_dir,)] + cmd |
| 277 elif self.options.tool != None: | 294 elif self.options.tool != None: |
| 278 raise LaunchFailure('Invalid tool name "%s"' % (self.options.tool,)) | 295 raise LaunchFailure('Invalid tool name "%s"' % (self.options.tool,)) |
| 279 cmd.extend(self.options.browser_flags) | 296 cmd.extend(self.options.browser_flags) |
| 280 cmd.append(url) | 297 cmd.append(url) |
| 281 return cmd | 298 return cmd |
| OLD | NEW |