[Windows] Fix error while launching subprocess on Windows Python
[WebKit-https.git] / Tools / Scripts / webkitpy / common / system / platforminfo.py
1 # Copyright (c) 2011 Google Inc. All rights reserved.
2 # Copyright (c) 2015-2017 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 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 import re
31 import sys
32
33 from webkitpy.common.version import Version
34 from webkitpy.common.system.executive import Executive
35
36
37 class PlatformInfo(object):
38     """This class provides a consistent (and mockable) interpretation of
39     system-specific values (like sys.platform and platform.mac_ver())
40     to be used by the rest of the webkitpy code base.
41
42     Public (static) properties:
43     -- os_name
44     -- os_version
45
46     Note that 'future' is returned for os_version if the operating system is
47     newer than one known to the code.
48     """
49
50     def __init__(self, sys_module, platform_module, executive):
51         self._executive = executive
52         self._platform_module = platform_module
53         self.os_name = self._determine_os_name(sys_module.platform)
54         if self.os_name == 'linux':
55             self.os_version = self._determine_linux_version()
56         if self.os_name == 'freebsd' or self.os_name == 'openbsd' or self.os_name == 'netbsd' or self.os_name == 'ios':
57             self.os_version = platform_module.release()
58         if self.os_name.startswith('mac'):
59             self.os_version = self._determine_mac_version(Version(platform_module.mac_ver()[0]))
60         if self.os_name.startswith('win'):
61             self.os_version = self._determine_win_version(self._win_version(sys_module))
62         self._is_cygwin = sys_module.platform == 'cygwin'
63
64     def is_mac(self):
65         return self.os_name == 'mac'
66
67     def is_ios(self):
68         return self.os_name == 'ios'
69
70     def is_win(self):
71         return self.os_name == 'win'
72
73     def is_native_win(self):
74         return self.is_win() and not self.is_cygwin()
75
76     def is_cygwin(self):
77         return self._is_cygwin
78
79     def is_linux(self):
80         return self.os_name == 'linux'
81
82     def is_freebsd(self):
83         return self.os_name == 'freebsd'
84
85     def is_openbsd(self):
86         return self.os_name == 'openbsd'
87
88     def is_netbsd(self):
89         return self.os_name == 'netbsd'
90
91     def display_name(self):
92         # platform.platform() returns Darwin information for Mac, which is just confusing.
93         if self.is_mac():
94             return "Mac OS X %s" % self._platform_module.mac_ver()[0]
95
96         # Returns strings like:
97         # Linux-2.6.18-194.3.1.el5-i686-with-redhat-5.5-Final
98         # Windows-2008ServerR2-6.1.7600
99         return self._platform_module.platform()
100
101     def total_bytes_memory(self):
102         if self.is_mac():
103             return long(self._executive.run_command(["sysctl", "-n", "hw.memsize"]))
104         return None
105
106     def terminal_width(self):
107         """Returns sys.maxint if the width cannot be determined."""
108         try:
109             if self.is_win():
110                 # From http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/
111                 from ctypes import windll, create_string_buffer
112                 handle = windll.kernel32.GetStdHandle(-12)  # -12 == stderr
113                 console_screen_buffer_info = create_string_buffer(22)  # 22 == sizeof(console_screen_buffer_info)
114                 if windll.kernel32.GetConsoleScreenBufferInfo(handle, console_screen_buffer_info):
115                     import struct
116                     _, _, _, _, _, left, _, right, _, _, _ = struct.unpack("hhhhHhhhhhh", console_screen_buffer_info.raw)
117                     # Note that we return 1 less than the width since writing into the rightmost column
118                     # automatically performs a line feed.
119                     return right - left
120                 return sys.maxint
121             else:
122                 import fcntl
123                 import struct
124                 import termios
125                 packed = fcntl.ioctl(sys.stderr.fileno(), termios.TIOCGWINSZ, '\0' * 8)
126                 _, columns, _, _ = struct.unpack('HHHH', packed)
127                 return columns
128         except:
129             return sys.maxint
130
131     def xcode_sdk_version(self, sdk_name):
132         if self.is_mac():
133             # Assumes that xcrun does not write to standard output on failure (e.g. SDK does not exist).
134             xcrun_output = self._executive.run_command(['xcrun', '--sdk', sdk_name, '--show-sdk-version'], return_stderr=False, error_handler=Executive.ignore_error).rstrip()
135             if xcrun_output:
136                 return Version(xcrun_output)
137         return None
138
139     def xcode_simctl_list(self):
140         if not self.is_mac():
141             return ()
142         output = self._executive.run_command(['xcrun', 'simctl', 'list'], return_stderr=False)
143         return (line for line in output.splitlines())
144
145     def xcode_version(self):
146         if not self.is_mac():
147             raise NotImplementedError
148         return Version(self._executive.run_command(['xcodebuild', '-version']).split()[1])
149
150     def _determine_os_name(self, sys_platform):
151         if sys_platform == 'darwin':
152             return 'mac'
153         if sys_platform == 'ios':
154             return 'ios'
155         if sys_platform.startswith('linux'):
156             return 'linux'
157         if sys_platform.startswith('win') or sys_platform == 'cygwin':
158             return 'win'
159         if sys_platform.startswith('freebsd'):
160             return 'freebsd'
161         if sys_platform.startswith('openbsd'):
162             return 'openbsd'
163         if sys_platform.startswith('haiku'):
164             return 'haiku'
165         raise AssertionError('unrecognized platform string "%s"' % sys_platform)
166
167     def _determine_mac_version(self, mac_version):
168         version_strings = {
169             5: 'leopard',
170             6: 'snowleopard',
171             7: 'lion',
172             8: 'mountainlion',
173             9: 'mavericks',
174             10: 'yosemite',
175             11: 'elcapitan',
176             12: 'sierra',
177             13: 'highsierra',
178         }
179         assert mac_version.minor >= min(version_strings.keys())
180         return version_strings.get(mac_version.minor, 'future')
181
182     def _determine_linux_version(self):
183         # FIXME: we ignore whatever the real version is and pretend it's lucid for now.
184         return 'lucid'
185
186     def _determine_win_version(self, win_version):
187         if self._platform_module.release() == '10':
188             return 'win10'
189         if win_version.major == 0 and win_version.minor == 0:
190             if win_version[2] > 10000:
191                 return 'win10'
192         if win_version == Version([6, 1, 7600]):
193             return '7sp0'
194         if win_version.major == 6 and win_version.minor == 0:
195             return 'vista'
196         if win_version.major == 5 and win_version.minor == 1:
197             return 'xp'
198         assert win_version[0] > 6 or win_version[1] >= 1, 'Unrecognized Windows version: "{}"'.format(win_version)
199         return 'future'
200
201     def _win_version(self, sys_module):
202         if hasattr(sys_module, 'getwindowsversion'):
203             return Version(sys_module.getwindowsversion()[0:3])
204         return Version(self._win_version_from_cmd())
205
206     def _win_version_from_cmd(self):
207         # Note that this should only ever be called on windows, so this should always work.
208         ver_output = self._executive.run_command(['cmd', '/c', 'ver'], decode_output=False)
209         match_object = re.search(r'(?P<major>\d)\.(?P<minor>\d)\.(?P<build>\d+)', ver_output)
210         assert match_object, 'cmd returned an unexpected version string: ' + ver_output
211         return match_object.groups()