1 # Copyright (C) 2010 Google Inc. All rights reserved. 2 # 3 # Redistribution and use in source and binary forms, with or without 4 # modification, are permitted provided that the following conditions are 5 # met: 6 # 7 # * Redistributions of source code must retain the above copyright 8 # notice, this list of conditions and the following disclaimer. 9 # * Redistributions in binary form must reproduce the above 10 # copyright notice, this list of conditions and the following disclaimer 11 # in the documentation and/or other materials provided with the 12 # distribution. 13 # * Neither the name of Google Inc. nor the names of its 14 # contributors may be used to endorse or promote products derived from 15 # this software without specific prior written permission. 16 # 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 """Windows implementation of the Port interface.""" 30 31 import errno 32 import os 33 import logging 34 35 try: 36 import _winreg 37 except ImportError as e: 38 _winreg = None 39 WindowsError = Exception # this shuts up pylint. 40 41 from webkitpy.layout_tests.breakpad.dump_reader_win import DumpReaderWin 42 from webkitpy.layout_tests.models import test_run_results 43 from webkitpy.layout_tests.port import base 44 from webkitpy.layout_tests.servers import crash_service 45 46 47 _log = logging.getLogger(__name__) 48 49 50 class WinPort(base.Port): 51 port_name = 'win' 52 53 # FIXME: Figure out how to unify this with base.TestConfiguration.all_systems()? 54 SUPPORTED_VERSIONS = ('xp', 'win7') 55 56 FALLBACK_PATHS = { 'win7': [ 'win' ]} 57 FALLBACK_PATHS['xp'] = ['win-xp'] + FALLBACK_PATHS['win7'] 58 59 DEFAULT_BUILD_DIRECTORIES = ('build', 'out') 60 61 BUILD_REQUIREMENTS_URL = 'http://www.chromium.org/developers/how-tos/build-instructions-windows' 62 63 @classmethod 64 def determine_full_port_name(cls, host, options, port_name): 65 if port_name.endswith('win'): 66 assert host.platform.is_win() 67 # We don't maintain separate baselines for vista, so we pretend it is win7. 68 if host.platform.os_version in ('vista', '7sp0', '7sp1', 'future'): 69 version = 'win7' 70 else: 71 version = host.platform.os_version 72 port_name = port_name + '-' + version 73 return port_name 74 75 def __init__(self, host, port_name, **kwargs): 76 super(WinPort, self).__init__(host, port_name, **kwargs) 77 self._version = port_name[port_name.index('win-') + len('win-'):] 78 assert self._version in self.SUPPORTED_VERSIONS, "%s is not in %s" % (self._version, self.SUPPORTED_VERSIONS) 79 if not self.get_option('disable_breakpad'): 80 self._dump_reader = DumpReaderWin(host, self._build_path()) 81 self._crash_service = None 82 self._crash_service_available = None 83 84 def additional_drt_flag(self): 85 flags = super(WinPort, self).additional_drt_flag() 86 flags += ['--enable-direct-write'] 87 if not self.get_option('disable_breakpad'): 88 flags += ['--enable-crash-reporter', '--crash-dumps-dir=%s' % self._dump_reader.crash_dumps_directory()] 89 return flags 90 91 def check_httpd(self): 92 res = super(WinPort, self).check_httpd() 93 if self.uses_apache(): 94 # In order to run CGI scripts on Win32 that use unix shebang lines, we need to 95 # create entries in the registry that remap the extensions (.pl and .cgi) to the 96 # appropriate Win32 paths. The command line arguments must match the command 97 # line arguments in the shebang line exactly. 98 if _winreg: 99 res = self._check_reg(r'.cgi\Shell\ExecCGI\Command') and res 100 res = self._check_reg(r'.pl\Shell\ExecCGI\Command') and res 101 else: 102 _log.warning("Could not check the registry; http may not work correctly.") 103 104 return res 105 106 def _check_reg(self, sub_key): 107 # see comments in check_httpd(), above, for why this routine exists and what it's doing. 108 try: 109 # Note that we HKCR is a union of HKLM and HKCR (with the latter 110 # overridding the former), so reading from HKCR ensures that we get 111 # the value if it is set in either place. See als comments below. 112 hkey = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, sub_key) 113 args = _winreg.QueryValue(hkey, '').split() 114 _winreg.CloseKey(hkey) 115 116 # In order to keep multiple checkouts from stepping on each other, we simply check that an 117 # existing entry points to a valid path and has the right command line. 118 if len(args) == 2 and self._filesystem.exists(args[0]) and args[0].endswith('perl.exe') and args[1] == '-wT': 119 return True 120 except WindowsError, e: 121 if e.errno != errno.ENOENT: 122 raise e 123 # The key simply probably doesn't exist. 124 pass 125 126 # Note that we write to HKCU so that we don't need privileged access 127 # to the registry, and that will get reflected in HKCR when it is read, above. 128 cmdline = self.path_from_chromium_base('third_party', 'perl', 'perl', 'bin', 'perl.exe') + ' -wT' 129 hkey = _winreg.CreateKeyEx(_winreg.HKEY_CURRENT_USER, 'Software\\Classes\\' + sub_key, 0, _winreg.KEY_WRITE) 130 _winreg.SetValue(hkey, '', _winreg.REG_SZ, cmdline) 131 _winreg.CloseKey(hkey) 132 return True 133 134 def setup_test_run(self): 135 super(WinPort, self).setup_test_run() 136 137 if not self.get_option('disable_breakpad'): 138 assert not self._crash_service, 'Already running a crash service' 139 if self._crash_service_available == None: 140 self._crash_service_available = self._check_crash_service_available() 141 if not self._crash_service_available: 142 return 143 service = crash_service.CrashService(self, self._dump_reader.crash_dumps_directory()) 144 service.start() 145 self._crash_service = service 146 147 def clean_up_test_run(self): 148 super(WinPort, self).clean_up_test_run() 149 150 if self._crash_service: 151 self._crash_service.stop() 152 self._crash_service = None 153 154 def setup_environ_for_server(self, server_name=None): 155 env = super(WinPort, self).setup_environ_for_server(server_name) 156 157 # FIXME: This is a temporary hack to get the cr-win bot online until 158 # someone from the cr-win port can take a look. 159 apache_envvars = ['SYSTEMDRIVE', 'SYSTEMROOT', 'TEMP', 'TMP'] 160 for key, value in os.environ.items(): 161 if key not in env and key in apache_envvars: 162 env[key] = value 163 164 # Put the cygwin directory first in the path to find cygwin1.dll. 165 env["PATH"] = "%s;%s" % (self.path_from_chromium_base("third_party", "cygwin", "bin"), env["PATH"]) 166 # Configure the cygwin directory so that pywebsocket finds proper 167 # python executable to run cgi program. 168 env["CYGWIN_PATH"] = self.path_from_chromium_base("third_party", "cygwin", "bin") 169 if self.get_option('register_cygwin'): 170 setup_mount = self.path_from_chromium_base("third_party", "cygwin", "setup_mount.bat") 171 self._executive.run_command([setup_mount]) # Paths are all absolute, so this does not require a cwd. 172 return env 173 174 def _modules_to_search_for_symbols(self): 175 # FIXME: we should return the path to the ffmpeg equivalents to detect if we have the mp3 and aac codecs installed. 176 # See https://bugs.webkit.org/show_bug.cgi?id=89706. 177 return [] 178 179 def check_build(self, needs_http, printer): 180 result = super(WinPort, self).check_build(needs_http, printer) 181 182 self._crash_service_available = self._check_crash_service_available() 183 if not self._crash_service_available: 184 result = test_run_results.UNEXPECTED_ERROR_EXIT_STATUS 185 186 if result: 187 _log.error('For complete Windows build requirements, please see:') 188 _log.error('') 189 _log.error(' http://dev.chromium.org/developers/how-tos/build-instructions-windows') 190 return result 191 192 def operating_system(self): 193 return 'win' 194 195 def relative_test_filename(self, filename): 196 path = filename[len(self.layout_tests_dir()) + 1:] 197 return path.replace('\\', '/') 198 199 def uses_apache(self): 200 val = self.get_option('use_apache') 201 if val is None: 202 return True 203 return val 204 205 def path_to_apache(self): 206 return self.path_from_chromium_base('third_party', 'apache-win32', 'bin', 'httpd.exe') 207 208 def path_to_apache_config_file(self): 209 return self._filesystem.join(self.layout_tests_dir(), 'http', 'conf', 'win-httpd.conf') 210 211 # 212 # PROTECTED ROUTINES 213 # 214 215 def _path_to_driver(self, configuration=None): 216 binary_name = '%s.exe' % self.driver_name() 217 return self._build_path_with_configuration(configuration, binary_name) 218 219 def _path_to_crash_service(self): 220 binary_name = 'content_shell_crash_service.exe' 221 return self._build_path(binary_name) 222 223 def _path_to_image_diff(self): 224 binary_name = 'image_diff.exe' 225 return self._build_path(binary_name) 226 227 def _path_to_wdiff(self): 228 return self.path_from_chromium_base('third_party', 'cygwin', 'bin', 'wdiff.exe') 229 230 def _check_crash_service_available(self): 231 """Checks whether the crash service binary is present.""" 232 result = self._check_file_exists(self._path_to_crash_service(), "content_shell_crash_service.exe") 233 if not result: 234 _log.error(" Could not find crash service, unexpected crashes won't be symbolized.") 235 _log.error(' Did you build the target blink_tests?') 236 _log.error('') 237 return result 238 239 def look_for_new_crash_logs(self, crashed_processes, start_time): 240 if self.get_option('disable_breakpad'): 241 return None 242 return self._dump_reader.look_for_new_crash_logs(crashed_processes, start_time) 243 244 def clobber_old_port_specific_results(self): 245 if not self.get_option('disable_breakpad'): 246 self._dump_reader.clobber_old_results() 247