23b999e2a4391c481bd4a442847f20deee6745d2
[WebKit-https.git] / Tools / Scripts / webkitpy / port / mac.py
1 # Copyright (C) 2011 Google Inc. All rights reserved.
2 # Copyright (C) 2012, 2013, 2016 Apple 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 Google name 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 import logging
31 import os
32 import time
33 import re
34
35 from webkitpy.common.memoized import memoized
36 from webkitpy.common.system.executive import ScriptError
37 from webkitpy.port.config import apple_additions
38 from webkitpy.port.darwin import DarwinPort
39
40 _log = logging.getLogger(__name__)
41
42
43 class MacPort(DarwinPort):
44     port_name = "mac"
45
46     VERSION_FALLBACK_ORDER = ['mac-snowleopard', 'mac-lion', 'mac-mountainlion', 'mac-mavericks', 'mac-yosemite', 'mac-elcapitan', 'mac-sierra', 'mac-highsierra']
47     SDK = 'macosx'
48
49     ARCHITECTURES = ['x86_64', 'x86']
50
51     DEFAULT_ARCHITECTURE = 'x86_64'
52
53     def __init__(self, host, port_name, **kwargs):
54         DarwinPort.__init__(self, host, port_name, **kwargs)
55         self._os_version = port_name.split('-')[1] if port_name.split('-') > 1 else self.host.platform.os_version
56
57     def _build_driver_flags(self):
58         return ['ARCHS=i386'] if self.architecture() == 'x86' else []
59
60     def _apple_additions_path(self, name):
61         if name == 'wk2':
62             return None
63         split_name = name.split('-')
64         if len(split_name) > 1 and split_name[1] != 'wk1' and split_name[1] != 'wk2':
65             os_name = apple_additions().mac_os_name(split_name[1])
66             if not os_name:
67                 return None
68             name = split_name[0] + '-' + os_name + ('-' + '-'.join(split_name[2:])) if len(split_name) > 2 else ''
69         return self._filesystem.join(apple_additions().layout_tests_path(), name)
70
71     @memoized
72     def default_baseline_search_path(self):
73         wk_string = 'wk1'
74         if self.get_option('webkit_test_runner'):
75             wk_string = 'wk2'
76         fallback_names = [
77             '{}-{}-{}'.format(self.port_name, self._os_version, wk_string),
78             '{}-{}'.format(self.port_name, self._os_version),
79             '{}-{}'.format(self.port_name, wk_string),
80             self.port_name,
81         ]
82         if self.get_option('webkit_test_runner'):
83             fallback_names.append('wk2')
84
85         webkit_expectations = map(self._webkit_baseline_path, fallback_names)
86         if apple_additions() and getattr(apple_additions(), "layout_tests_path", None):
87             apple_expectations = map(self._apple_additions_path, fallback_names)
88             result = []
89             for i in xrange(len(webkit_expectations)):
90                 if apple_expectations[i]:
91                     result.append(apple_expectations[i])
92                 result.append(webkit_expectations[i])
93             return result
94         return webkit_expectations
95
96     def configuration_specifier_macros(self):
97         return {
98             "highsierra+": ["highsierra", "future"],
99             "sierra+": ["sierra", "highsierra", "future"],
100             "elcapitan+": ["elcapitan", "sierra", "highsierra", "future"],
101             "yosemite+": ["yosemite", "elcapitan", "sierra", "highsierra", "future"],
102         }
103
104     def setup_environ_for_server(self, server_name=None):
105         env = super(MacPort, self).setup_environ_for_server(server_name)
106         if server_name == self.driver_name():
107             if self.get_option('leaks'):
108                 env['MallocStackLogging'] = '1'
109                 env['__XPC_MallocStackLogging'] = '1'
110                 env['MallocScribble'] = '1'
111                 env['__XPC_MallocScribble'] = '1'
112             if self.get_option('guard_malloc'):
113                 self._append_value_colon_separated(env, 'DYLD_INSERT_LIBRARIES', '/usr/lib/libgmalloc.dylib')
114                 self._append_value_colon_separated(env, '__XPC_DYLD_INSERT_LIBRARIES', '/usr/lib/libgmalloc.dylib')
115             self._append_value_colon_separated(env, 'DYLD_INSERT_LIBRARIES', self._build_path("libWebCoreTestShim.dylib"))
116         env['XML_CATALOG_FILES'] = ''  # work around missing /etc/catalog <rdar://problem/4292995>
117         return env
118
119     def _clear_global_caches_and_temporary_files(self):
120         self._filesystem.rmtree(os.path.expanduser('~/Library/' + self.driver_name()))
121         self._filesystem.rmtree(os.path.expanduser('~/Library/Application Support/' + self.driver_name()))
122         self._filesystem.rmtree(os.path.expanduser('~/Library/Caches/' + self.driver_name()))
123         self._filesystem.rmtree(os.path.expanduser('~/Library/WebKit/' + self.driver_name()))
124
125     def _path_to_user_cache_directory(self, suffix=None):
126         DIRHELPER_USER_DIR_SUFFIX = 'DIRHELPER_USER_DIR_SUFFIX'
127         CS_DARWIN_USER_CACHE_DIR = 65538
128
129         # The environment variable DIRHELPER_USER_DIR_SUFFIX is only honored on systems with
130         # System Integrity Protection disabled or with an Apple-Internal OS. To make this code
131         # work for all system configurations we compute the path with respect to the suffix
132         # by hand and temporarily unset the environment variable DIRHELPER_USER_DIR_SUFFIX (if set)
133         # to avoid it influencing confstr() on systems that honor DIRHELPER_USER_DIR_SUFFIX.
134         saved_suffix = None
135         if DIRHELPER_USER_DIR_SUFFIX in os.environ:
136             saved_suffix = os.environ[DIRHELPER_USER_DIR_SUFFIX]
137             del os.environ[DIRHELPER_USER_DIR_SUFFIX]
138         result = os.path.join(os.confstr(CS_DARWIN_USER_CACHE_DIR), suffix or '')
139         if saved_suffix is not None:
140             os.environ[DIRHELPER_USER_DIR_SUFFIX] = saved_suffix
141         return result
142
143     def operating_system(self):
144         return 'mac'
145
146     # Belongs on a Platform object.
147     def is_mavericks(self):
148         return self._version == 'mavericks'
149
150     def default_child_processes(self):
151         default_count = super(MacPort, self).default_child_processes()
152
153         # FIXME: https://bugs.webkit.org/show_bug.cgi?id=95906  With too many WebProcess WK2 tests get stuck in resource contention.
154         # To alleviate the issue reduce the number of running processes
155         # Anecdotal evidence suggests that a 4 core/8 core logical machine may run into this, but that a 2 core/4 core logical machine does not.
156         should_throttle_for_wk2 = self.get_option('webkit_test_runner') and default_count > 4
157         # We also want to throttle for leaks bots.
158         if should_throttle_for_wk2 or self.get_option('leaks'):
159             default_count = int(.75 * default_count)
160
161         if should_throttle_for_wk2 and self.get_option('guard_malloc'):
162             # Some 12 core Macs get a lot of tests time out when running 18 WebKitTestRunner processes (it's not clear what this depends on).
163             # <rdar://problem/25750302>
164             default_count = min(default_count, 12)
165
166         # Make sure we have enough ram to support that many instances:
167         total_memory = self.host.platform.total_bytes_memory()
168         if total_memory:
169             bytes_per_drt = 256 * 1024 * 1024  # Assume each DRT needs 256MB to run.
170             overhead = 2048 * 1024 * 1024  # Assume we need 2GB free for the O/S
171             supportable_instances = max((total_memory - overhead) / bytes_per_drt, 1)  # Always use one process, even if we don't have space for it.
172             if supportable_instances < default_count:
173                 _log.warning("This machine could support %s child processes, but only has enough memory for %s." % (default_count, supportable_instances))
174         else:
175             _log.warning("Cannot determine available memory for child processes, using default child process count of %s." % default_count)
176             supportable_instances = default_count
177         return min(supportable_instances, default_count)
178
179     def _build_java_test_support(self):
180         # FIXME: This is unused. Remove.
181         java_tests_path = self._filesystem.join(self.layout_tests_dir(), "java")
182         build_java = [self.make_command(), "-C", java_tests_path]
183         if self._executive.run_command(build_java, return_exit_code=True):  # Paths are absolute, so we don't need to set a cwd.
184             _log.error("Failed to build Java support files: %s" % build_java)
185             return False
186         return True
187
188     def _check_port_build(self):
189         return not self.get_option('java') or self._build_java_test_support()
190
191     def start_helper(self, pixel_tests=False):
192         helper_path = self._path_to_helper()
193         if not helper_path:
194             _log.error("No path to LayoutTestHelper binary")
195             return False
196         _log.debug("Starting layout helper %s" % helper_path)
197         arguments = [helper_path, '--install-color-profile']
198         self._helper = self._executive.popen(arguments,
199             stdin=self._executive.PIPE, stdout=self._executive.PIPE, stderr=None)
200         is_ready = self._helper.stdout.readline()
201         if not is_ready.startswith('ready'):
202             _log.error("LayoutTestHelper could not start")
203             return False
204         return True
205
206     def reset_preferences(self):
207         _log.debug("Resetting persistent preferences")
208
209         for domain in ["DumpRenderTree", "WebKitTestRunner"]:
210             try:
211                 self._executive.run_command(["defaults", "delete", domain])
212             except ScriptError, e:
213                 # 'defaults' returns 1 if the domain did not exist
214                 if e.exit_code != 1:
215                     raise e
216
217     def stop_helper(self):
218         if self._helper:
219             _log.debug("Stopping LayoutTestHelper")
220             try:
221                 self._helper.stdin.write("x\n")
222                 self._helper.stdin.close()
223                 self._helper.wait()
224             except IOError, e:
225                 _log.debug("IOError raised while stopping helper: %s" % str(e))
226             self._helper = None
227
228     def logging_patterns_to_strip(self):
229         # FIXME: Remove this after <rdar://problem/15605007> is fixed
230         return [(re.compile('(AVF|GVA) info:.*\n'), '')]
231
232     def stderr_patterns_to_strip(self):
233         worthless_patterns = []
234         worthless_patterns.append((re.compile('.*(Fig|fig|itemasync|vt|mv_|PullParamSetSPS|ccrp_|client).* signalled err=.*\n'), ''))
235         worthless_patterns.append((re.compile('.*<<<< FigFilePlayer >>>>.*\n'), ''))
236         worthless_patterns.append((re.compile('.*<<<< FigFile >>>>.*\n'), ''))
237         worthless_patterns.append((re.compile('.*<<<< FAQ >>>>.*\n'), ''))
238         worthless_patterns.append((re.compile('.*<<<< MediaValidator >>>>.*\n'), ''))
239         worthless_patterns.append((re.compile('.*<<<< VMC >>>>.*\n'), ''))
240         worthless_patterns.append((re.compile('.*<<< FFR_Common >>>.*\n'), ''))
241         return worthless_patterns