Home | History | Annotate | Download | only in port
      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