[webkitpy] PlatformInfo should have default argument for casual use
[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 import platform
33
34 from webkitpy.common.version import Version
35 from webkitpy.common.version_name_map import PUBLIC_TABLE, VersionNameMap
36 from webkitpy.common.system.executive import Executive
37
38
39 class PlatformInfo(object):
40     """This class provides a consistent (and mockable) interpretation of
41     system-specific values (like sys.platform and platform.mac_ver())
42     to be used by the rest of the webkitpy code base.
43
44     Public (static) properties:
45     -- os_name
46     -- os_version
47
48     Note that 'future' is returned for os_version if the operating system is
49     newer than one known to the code.
50     """
51
52     def __init__(self, sys_module=None, platform_module=None, executive=None):
53         sys_platform = (sys_module or sys).platform
54         platform_module = platform_module or platform
55
56         self._executive = executive
57         self._platform_module = platform_module
58         self.os_name = self._determine_os_name(sys_platform)
59         self.os_version = None
60
61         self._is_cygwin = sys_platform == 'cygwin'
62
63         if self.os_name.startswith('mac'):
64             self.os_version = Version.from_string(platform_module.mac_ver()[0])
65         elif self.os_name.startswith('win'):
66             self.os_version = self._win_version()
67         elif self.os_name == 'linux' or self.os_name == 'freebsd' or self.os_name == 'openbsd' or self.os_name == 'netbsd':
68             return
69         else:
70             # Most other platforms (namely iOS) return conforming version strings.
71             self.os_version = Version.from_string(platform_module.release())
72
73     @property
74     def executive(self):
75         if self._executive is None:
76             self._executive = Executive()
77
78         return self._executive
79
80     def is_mac(self):
81         return self.os_name == 'mac'
82
83     def is_ios(self):
84         return self.os_name == 'ios'
85
86     def is_win(self):
87         return self.os_name == 'win'
88
89     def is_native_win(self):
90         return self.is_win() and not self.is_cygwin()
91
92     def is_cygwin(self):
93         return self._is_cygwin
94
95     def is_linux(self):
96         return self.os_name == 'linux'
97
98     def is_freebsd(self):
99         return self.os_name == 'freebsd'
100
101     def is_openbsd(self):
102         return self.os_name == 'openbsd'
103
104     def is_netbsd(self):
105         return self.os_name == 'netbsd'
106
107     def display_name(self):
108         # platform.platform() returns Darwin information for Mac, which is just confusing.
109         if self.is_mac():
110             return "Mac OS X %s" % self._platform_module.mac_ver()[0]
111
112         # Returns strings like:
113         # Linux-2.6.18-194.3.1.el5-i686-with-redhat-5.5-Final
114         # Windows-2008ServerR2-6.1.7600
115         return self._platform_module.platform()
116
117     def os_version_name(self, table=PUBLIC_TABLE):
118         if not self.os_version:
119             return None
120         return VersionNameMap.map(self).to_name(self.os_version, table=table)
121
122     def total_bytes_memory(self):
123         if self.is_mac():
124             return long(self.executive.run_command(["sysctl", "-n", "hw.memsize"]))
125         return None
126
127     def terminal_width(self):
128         """Returns sys.maxint if the width cannot be determined."""
129         try:
130             if self.is_win():
131                 # From http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/
132                 from ctypes import windll, create_string_buffer
133                 handle = windll.kernel32.GetStdHandle(-12)  # -12 == stderr
134                 console_screen_buffer_info = create_string_buffer(22)  # 22 == sizeof(console_screen_buffer_info)
135                 if windll.kernel32.GetConsoleScreenBufferInfo(handle, console_screen_buffer_info):
136                     import struct
137                     _, _, _, _, _, left, _, right, _, _, _ = struct.unpack("hhhhHhhhhhh", console_screen_buffer_info.raw)
138                     # Note that we return 1 less than the width since writing into the rightmost column
139                     # automatically performs a line feed.
140                     return right - left
141                 return sys.maxint
142             else:
143                 import fcntl
144                 import struct
145                 import termios
146                 packed = fcntl.ioctl(sys.stderr.fileno(), termios.TIOCGWINSZ, '\0' * 8)
147                 _, columns, _, _ = struct.unpack('HHHH', packed)
148                 return columns
149         except:
150             return sys.maxint
151
152     def xcode_sdk_version(self, sdk_name):
153         if self.is_mac():
154             # Assumes that xcrun does not write to standard output on failure (e.g. SDK does not exist).
155             xcrun_output = self.executive.run_command(['xcrun', '--sdk', sdk_name, '--show-sdk-version'], return_stderr=False, error_handler=Executive.ignore_error).rstrip()
156             if xcrun_output:
157                 return Version.from_string(xcrun_output)
158         return None
159
160     def xcode_simctl_list(self):
161         if not self.is_mac():
162             return ()
163         output = self.executive.run_command(['xcrun', 'simctl', 'list'], return_stderr=False)
164         return (line for line in output.splitlines())
165
166     def xcode_version(self):
167         if not self.is_mac():
168             raise NotImplementedError
169         return Version.from_string(self.executive.run_command(['xcodebuild', '-version']).split()[1])
170
171     def _determine_os_name(self, sys_platform):
172         if sys_platform == 'darwin':
173             return 'mac'
174         if sys_platform == 'ios':
175             return 'ios'
176         if sys_platform.startswith('linux'):
177             return 'linux'
178         if sys_platform.startswith('win') or sys_platform == 'cygwin':
179             return 'win'
180         if sys_platform.startswith('freebsd'):
181             return 'freebsd'
182         if sys_platform.startswith('openbsd'):
183             return 'openbsd'
184         if sys_platform.startswith('haiku'):
185             return 'haiku'
186         raise AssertionError('unrecognized platform string "%s"' % sys_platform)
187
188     def _win_version(self):
189         version = self._win_version_str()
190         match_object = re.search(r'(?P<major>\d+)\.(?P<minor>\d+)\.(?P<build>\d+)', version)
191         assert match_object, 'cmd returned an unexpected version string: ' + version
192         return Version.from_iterable(match_object.groups())
193
194     def _win_version_str(self):
195         version = self._platform_module.win32_ver()[1]
196         if version:
197             return version
198         # Note that this should only ever be called on windows, so this should always work.
199         return self.executive.run_command(['cmd', '/c', 'ver'], decode_output=False)