• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Linux implementation of the Port interface."""
31
32import logging
33import os
34import signal
35
36import chromium
37
38_log = logging.getLogger("webkitpy.layout_tests.port.chromium_linux")
39
40
41class ChromiumLinuxPort(chromium.ChromiumPort):
42    """Chromium Linux implementation of the Port class."""
43    SUPPORTED_ARCHITECTURES = ('x86', 'x86_64')
44
45    FALLBACK_PATHS = {
46        'x86_64': ['chromium-linux-x86_64', 'chromium-linux', 'chromium-win', 'chromium', 'win', 'mac'],
47        'x86': ['chromium-linux', 'chromium-win', 'chromium', 'win', 'mac'],
48    }
49
50    def __init__(self, port_name=None, **kwargs):
51        port_name = port_name or 'chromium-linux'
52        chromium.ChromiumPort.__init__(self, port_name=port_name, **kwargs)
53
54        # We re-set the port name once the base object is fully initialized
55        # in order to be able to find the DRT binary properly.
56        if port_name.endswith('-linux'):
57            self._architecture = self._determine_architecture()
58            # FIXME: this is an ugly hack to avoid renaming the GPU port.
59            if port_name == 'chromium-linux':
60                port_name = port_name + '-' + self._architecture
61        else:
62            base, arch = port_name.rsplit('-', 1)
63            assert base in ('chromium-linux', 'chromium-gpu-linux')
64            self._architecture = arch
65        assert self._architecture in self.SUPPORTED_ARCHITECTURES
66        assert port_name in ('chromium-linux', 'chromium-gpu-linux',
67                             'chromium-linux-x86', 'chromium-linux-x86_64',
68                             'chromium-gpu-linux-x86_64')
69        self._name = port_name
70        self._operating_system = 'linux'
71        # FIXME: add support for 'lucid'
72        self._version = 'hardy'
73
74    def _determine_architecture(self):
75        driver_path = self._path_to_driver()
76        file_output = ''
77        if self._filesystem.exists(driver_path):
78            file_output = self._executive.run_command(['file', driver_path],
79                                                      return_stderr=True)
80
81        if 'ELF 32-bit LSB executable' in file_output:
82            return 'x86'
83        if 'ELF 64-bit LSB executable' in file_output:
84            return 'x86_64'
85        if file_output:
86            _log.warning('Could not determine architecture from "file" output: %s' % file_output)
87
88        # We don't know what the architecture is; default to 'x86' because
89        # maybe we're rebaselining and the binary doesn't actually exist,
90        # or something else weird is going on. It's okay to do this because
91        # if we actually try to use the binary, check_build() should fail.
92        return 'x86'
93
94    def baseline_path(self):
95        if self._architecture == 'x86_64':
96            return self._webkit_baseline_path(self._name)
97        return self._webkit_baseline_path('chromium-linux')
98
99    def baseline_search_path(self):
100        port_names = self.FALLBACK_PATHS[self._architecture]
101        return map(self._webkit_baseline_path, port_names)
102
103    def check_build(self, needs_http):
104        result = chromium.ChromiumPort.check_build(self, needs_http)
105        if needs_http:
106            if self.get_option('use_apache'):
107                result = self._check_apache_install() and result
108            else:
109                result = self._check_lighttpd_install() and result
110        result = self._check_wdiff_install() and result
111
112        if not result:
113            _log.error('For complete Linux build requirements, please see:')
114            _log.error('')
115            _log.error('    http://code.google.com/p/chromium/wiki/'
116                       'LinuxBuildInstructions')
117        return result
118
119    #
120    # PROTECTED METHODS
121    #
122
123    def _build_path(self, *comps):
124        if self.get_option('build_directory'):
125            return self._filesystem.join(self.get_option('build_directory'),
126                                         *comps)
127
128        base = self.path_from_chromium_base()
129        if self._filesystem.exists(self._filesystem.join(base, 'sconsbuild')):
130            return self._filesystem.join(base, 'sconsbuild', *comps)
131        if self._filesystem.exists(self._filesystem.join(base, 'out', *comps)):
132            return self._filesystem.join(base, 'out', *comps)
133        base = self.path_from_webkit_base()
134        if self._filesystem.exists(self._filesystem.join(base, 'sconsbuild')):
135            return self._filesystem.join(base, 'sconsbuild', *comps)
136        return self._filesystem.join(base, 'out', *comps)
137
138    def _check_apache_install(self):
139        result = self._check_file_exists(self._path_to_apache(),
140            "apache2")
141        result = self._check_file_exists(self._path_to_apache_config_file(),
142            "apache2 config file") and result
143        if not result:
144            _log.error('    Please install using: "sudo apt-get install '
145                       'apache2 libapache2-mod-php5"')
146            _log.error('')
147        return result
148
149    def _check_lighttpd_install(self):
150        result = self._check_file_exists(
151            self._path_to_lighttpd(), "LigHTTPd executable")
152        result = self._check_file_exists(self._path_to_lighttpd_php(),
153            "PHP CGI executable") and result
154        result = self._check_file_exists(self._path_to_lighttpd_modules(),
155            "LigHTTPd modules") and result
156        if not result:
157            _log.error('    Please install using: "sudo apt-get install '
158                       'lighttpd php5-cgi"')
159            _log.error('')
160        return result
161
162    def _check_wdiff_install(self):
163        result = self._check_file_exists(self._path_to_wdiff(), 'wdiff')
164        if not result:
165            _log.error('    Please install using: "sudo apt-get install '
166                       'wdiff"')
167            _log.error('')
168        # FIXME: The ChromiumMac port always returns True.
169        return result
170
171    def _path_to_apache(self):
172        if self._is_redhat_based():
173            return '/usr/sbin/httpd'
174        else:
175            return '/usr/sbin/apache2'
176
177    def _path_to_apache_config_file(self):
178        if self._is_redhat_based():
179            config_name = 'fedora-httpd.conf'
180        else:
181            config_name = 'apache2-debian-httpd.conf'
182
183        return self._filesystem.join(self.layout_tests_dir(), 'http', 'conf',
184                            config_name)
185
186    def _path_to_lighttpd(self):
187        return "/usr/sbin/lighttpd"
188
189    def _path_to_lighttpd_modules(self):
190        return "/usr/lib/lighttpd"
191
192    def _path_to_lighttpd_php(self):
193        return "/usr/bin/php-cgi"
194
195    def _path_to_driver(self, configuration=None):
196        if not configuration:
197            configuration = self.get_option('configuration')
198        binary_name = 'DumpRenderTree'
199        return self._build_path(configuration, binary_name)
200
201    def _path_to_helper(self):
202        return None
203
204    def _path_to_wdiff(self):
205        if self._is_redhat_based():
206            return '/usr/bin/dwdiff'
207        else:
208            return '/usr/bin/wdiff'
209
210    def _is_redhat_based(self):
211        return self._filesystem.exists(self._filesystem.join('/etc', 'redhat-release'))
212
213    def _shut_down_http_server(self, server_pid):
214        """Shut down the lighttpd web server. Blocks until it's fully
215        shut down.
216
217        Args:
218            server_pid: The process ID of the running server.
219        """
220        # server_pid is not set when "http_server.py stop" is run manually.
221        if server_pid is None:
222            # TODO(mmoss) This isn't ideal, since it could conflict with
223            # lighttpd processes not started by http_server.py,
224            # but good enough for now.
225            self._executive.kill_all("lighttpd")
226            self._executive.kill_all("apache2")
227        else:
228            try:
229                os.kill(server_pid, signal.SIGTERM)
230                # TODO(mmoss) Maybe throw in a SIGKILL just to be sure?
231            except OSError:
232                # Sometimes we get a bad PID (e.g. from a stale httpd.pid
233                # file), so if kill fails on the given PID, just try to
234                # 'killall' web servers.
235                self._shut_down_http_server(None)
236