1#!/usr/bin/env python 2# Copyright (C) 2010 Google Inc. All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: 7# 8# * Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# * Redistributions in binary form must reproduce the above 11# copyright notice, this list of conditions and the following disclaimer 12# in the documentation and/or other materials provided with the 13# distribution. 14# * Neither the name of Google Inc. nor the names of its 15# contributors may be used to endorse or promote products derived from 16# this software without specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30"""Chromium Mac implementation of the Port interface.""" 31 32import logging 33import os 34import signal 35 36from webkitpy.layout_tests.port import mac 37from webkitpy.layout_tests.port import chromium 38 39from webkitpy.common.system.executive import Executive 40 41_log = logging.getLogger("webkitpy.layout_tests.port.chromium_mac") 42 43 44class ChromiumMacPort(chromium.ChromiumPort): 45 """Chromium Mac implementation of the Port class.""" 46 SUPPORTED_OS_VERSIONS = ('leopard', 'snowleopard', 'future') 47 48 FALLBACK_PATHS = { 49 'leopard': [ 50 'chromium-mac-leopard', 51 'chromium-mac', 52 'chromium', 53 'mac-leopard', 54 'mac-snowleopard', 55 'mac', 56 ], 57 'snowleopard': [ 58 'chromium-mac', 59 'chromium', 60 'mac-snowleopard', 61 'mac', 62 ], 63 'future': [ 64 'chromium-mac', 65 'chromium', 66 'mac', 67 ], 68 } 69 70 def __init__(self, port_name=None, os_version_string=None, **kwargs): 71 # We're a little generic here because this code is reused by the 72 # 'google-chrome' port as well as the 'mock-' and 'dryrun-' ports. 73 port_name = port_name or 'chromium-mac' 74 chromium.ChromiumPort.__init__(self, port_name=port_name, **kwargs) 75 if port_name.endswith('-mac'): 76 self._version = mac.os_version(os_version_string, self.SUPPORTED_OS_VERSIONS) 77 self._name = port_name + '-' + self._version 78 else: 79 self._version = port_name[port_name.index('-mac-') + 5:] 80 assert self._version in self.SUPPORTED_OS_VERSIONS 81 self._operating_system = 'mac' 82 83 def baseline_path(self): 84 if self.version() in ('snowleopard', 'future'): 85 # We treat Snow Leopard as the newest version of mac, 86 # so it gets the base dir. 87 return self._webkit_baseline_path('chromium-mac') 88 return self._webkit_baseline_path(self.name()) 89 90 def baseline_search_path(self): 91 return map(self._webkit_baseline_path, self.FALLBACK_PATHS[self._version]) 92 93 def check_build(self, needs_http): 94 result = chromium.ChromiumPort.check_build(self, needs_http) 95 result = self._check_wdiff_install() and result 96 if not result: 97 _log.error('For complete Mac build requirements, please see:') 98 _log.error('') 99 _log.error(' http://code.google.com/p/chromium/wiki/' 100 'MacBuildInstructions') 101 return result 102 103 def default_child_processes(self): 104 if not self._multiprocessing_is_available: 105 # Running multiple threads in Mac Python is unstable (See 106 # https://bugs.webkit.org/show_bug.cgi?id=38553 for more info). 107 return 1 108 return chromium.ChromiumPort.default_child_processes(self) 109 110 def driver_name(self): 111 return "DumpRenderTree" 112 113 # 114 # PROTECTED METHODS 115 # 116 117 def _build_path(self, *comps): 118 if self.get_option('build_directory'): 119 return self._filesystem.join(self.get_option('build_directory'), 120 *comps) 121 122 path = self.path_from_chromium_base('xcodebuild', *comps) 123 if self._filesystem.exists(path): 124 return path 125 return self.path_from_webkit_base( 126 'Source', 'WebKit', 'chromium', 'xcodebuild', *comps) 127 128 def _check_wdiff_install(self): 129 try: 130 # We're ignoring the return and always returning True 131 self._executive.run_command([self._path_to_wdiff()], error_handler=Executive.ignore_error) 132 except OSError: 133 _log.warning('wdiff not found. Install using MacPorts or some ' 134 'other means') 135 return True 136 137 def _lighttpd_path(self, *comps): 138 return self.path_from_chromium_base('third_party', 'lighttpd', 139 'mac', *comps) 140 141 def _path_to_apache(self): 142 return '/usr/sbin/httpd' 143 144 def _path_to_apache_config_file(self): 145 return self._filesystem.join(self.layout_tests_dir(), 'http', 'conf', 146 'apache2-httpd.conf') 147 148 def _path_to_lighttpd(self): 149 return self._lighttpd_path('bin', 'lighttpd') 150 151 def _path_to_lighttpd_modules(self): 152 return self._lighttpd_path('lib') 153 154 def _path_to_lighttpd_php(self): 155 return self._lighttpd_path('bin', 'php-cgi') 156 157 def _path_to_driver(self, configuration=None): 158 # FIXME: make |configuration| happy with case-sensitive file 159 # systems. 160 if not configuration: 161 configuration = self.get_option('configuration') 162 return self._build_path(configuration, self.driver_name() + '.app', 163 'Contents', 'MacOS', self.driver_name()) 164 165 def _path_to_helper(self): 166 binary_name = 'LayoutTestHelper' 167 return self._build_path(self.get_option('configuration'), binary_name) 168 169 def _path_to_wdiff(self): 170 return 'wdiff' 171 172 def _shut_down_http_server(self, server_pid): 173 """Shut down the lighttpd web server. Blocks until it's fully 174 shut down. 175 176 Args: 177 server_pid: The process ID of the running server. 178 """ 179 # server_pid is not set when "http_server.py stop" is run manually. 180 if server_pid is None: 181 # TODO(mmoss) This isn't ideal, since it could conflict with 182 # lighttpd processes not started by http_server.py, 183 # but good enough for now. 184 self._executive.kill_all('lighttpd') 185 self._executive.kill_all('httpd') 186 else: 187 try: 188 os.kill(server_pid, signal.SIGTERM) 189 # TODO(mmoss) Maybe throw in a SIGKILL just to be sure? 190 except OSError: 191 # Sometimes we get a bad PID (e.g. from a stale httpd.pid 192 # file), so if kill fails on the given PID, just try to 193 # 'killall' web servers. 194 self._shut_down_http_server(None) 195