OLD | NEW |
1 # Copyright 2015 The Swarming Authors. All rights reserved. | 1 # Copyright 2015 The Swarming Authors. All rights reserved. |
2 # Use of this source code is governed by the Apache v2.0 license that can be | 2 # Use of this source code is governed by the Apache v2.0 license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Windows specific utility functions.""" | 5 """Windows specific utility functions.""" |
6 | 6 |
7 import ctypes | 7 import ctypes |
8 import logging | 8 import logging |
9 import os | 9 import os |
10 import platform | 10 import platform |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
75 # win32com is included in pywin32, which is an optional package that is | 75 # win32com is included in pywin32, which is an optional package that is |
76 # installed by Swarming devs. If you find yourself needing it to run without | 76 # installed by Swarming devs. If you find yourself needing it to run without |
77 # pywin32, for example in cygwin, please send us a CL with the | 77 # pywin32, for example in cygwin, please send us a CL with the |
78 # implementation that doesn't use pywin32. | 78 # implementation that doesn't use pywin32. |
79 return None | 79 return None |
80 | 80 |
81 wmi_service = win32com.client.Dispatch('WbemScripting.SWbemLocator') | 81 wmi_service = win32com.client.Dispatch('WbemScripting.SWbemLocator') |
82 return wmi_service.ConnectServer('.', 'root\\cimv2') | 82 return wmi_service.ConnectServer('.', 'root\\cimv2') |
83 | 83 |
84 | 84 |
| 85 @tools.cached |
| 86 def _get_os_numbers(): |
| 87 """Returns the normalized OS version and build numbers as strings. |
| 88 |
| 89 Actively work around AppCompat version lie shim. |
| 90 |
| 91 Returns: |
| 92 - 5.1, 6.1, etc. There is no way to distinguish between Windows 7 |
| 93 and Windows Server 2008R2 since they both report 6.1. |
| 94 """ |
| 95 # Windows is lying to us until python adds to its manifest: |
| 96 # <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> |
| 97 # and it doesn't. |
| 98 # So ask nicely to cmd.exe instead, which will always happily report the right |
| 99 # version. Here's some sample output: |
| 100 # - XP: Microsoft Windows XP [Version 5.1.2600] |
| 101 # - Win10: Microsoft Windows [Version 10.0.10240] |
| 102 # - Win7 or Win2K8R2: Microsoft Windows [Version 6.1.7601] |
| 103 out = subprocess.check_output(['cmd.exe', '/c', 'ver']).strip() |
| 104 match = re.search(r'\[Version (\d+\.\d+)\.(\d+)\]', out, re.IGNORECASE) |
| 105 return match.group(1), match.group(2) |
| 106 |
| 107 |
85 ## Public API. | 108 ## Public API. |
86 | 109 |
87 | 110 |
88 def from_cygwin_path(path): | 111 def from_cygwin_path(path): |
89 """Converts an absolute cygwin path to a standard Windows path.""" | 112 """Converts an absolute cygwin path to a standard Windows path.""" |
90 if not path.startswith('/cygdrive/'): | 113 if not path.startswith('/cygdrive/'): |
91 logging.error('%s is not a cygwin path', path) | 114 logging.error('%s is not a cygwin path', path) |
92 return None | 115 return None |
93 | 116 |
94 # Remove the cygwin path identifier. | 117 # Remove the cygwin path identifier. |
(...skipping 10 matching lines...) Expand all Loading... |
105 # TODO(maruel): Accept \\?\ and \??\ if necessary. | 128 # TODO(maruel): Accept \\?\ and \??\ if necessary. |
106 logging.error('%s is not a win32 path', path) | 129 logging.error('%s is not a win32 path', path) |
107 return None | 130 return None |
108 return '/cygdrive/%s/%s' % (path[0].lower(), path[3:].replace('\\', '/')) | 131 return '/cygdrive/%s/%s' % (path[0].lower(), path[3:].replace('\\', '/')) |
109 | 132 |
110 | 133 |
111 @tools.cached | 134 @tools.cached |
112 def get_os_version_number(): | 135 def get_os_version_number(): |
113 """Returns the normalized OS version number as a string. | 136 """Returns the normalized OS version number as a string. |
114 | 137 |
115 Actively work around AppCompat version lie shim. | |
116 | |
117 Returns: | 138 Returns: |
118 - 5.1, 6.1, etc. There is no way to distinguish between Windows 7 | 139 - '5.1', '6.1', '10.0', etc. There is no way to distinguish between Windows |
119 and Windows Server 2008R2 since they both report 6.1. | 140 7 and Windows Server 2008R2 since they both report 6.1. |
120 """ | 141 """ |
121 # Windows is lying to us until python adds to its manifest: | 142 return _get_os_numbers()[0] |
122 # <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | |
123 # and it doesn't. | |
124 # So ask nicely to cmd.exe instead, which will always happily report the right | |
125 # version. Here's some sample output: | |
126 # - XP: Microsoft Windows XP [Version 5.1.2600] | |
127 # - Win10: Microsoft Windows [Version 10.0.10240] | |
128 # - Win7 or Win2K8R2: Microsoft Windows [Version 6.1.7601] | |
129 out = subprocess.check_output(['cmd.exe', '/c', 'ver']).strip() | |
130 return re.search(r'\[Version (\d+\.\d+)\.\d+\]', out, re.IGNORECASE).group(1) | |
131 | 143 |
132 | 144 |
133 @tools.cached | 145 @tools.cached |
134 def get_os_version_name(): | 146 def get_os_version_name(): |
135 """Returns the marketing name of the OS including the service pack.""" | 147 """Returns the marketing name of the OS including the service pack. |
| 148 |
| 149 On Windows 10, use the build number since there will be no service pack. |
| 150 """ |
136 # Python keeps a local map in platform.py and it is updated at newer python | 151 # Python keeps a local map in platform.py and it is updated at newer python |
137 # release. Since our python release is a bit old, do not rely on it. | 152 # release. Since our python release is a bit old, do not rely on it. |
138 is_server = sys.getwindowsversion().product_type == 3 | 153 is_server = sys.getwindowsversion().product_type == 3 |
139 lookup = _WIN32_SERVER_NAMES if is_server else _WIN32_CLIENT_NAMES | 154 lookup = _WIN32_SERVER_NAMES if is_server else _WIN32_CLIENT_NAMES |
140 version_number = get_os_version_number() | 155 version_number, build_number = _get_os_numbers() |
141 marketing_name = lookup.get(version_number, version_number) | 156 marketing_name = lookup.get(version_number, version_number) |
| 157 if version_number == '10.0': |
| 158 # Windows 10 doesn't have service packs, the build number now is the |
| 159 # reference number. |
| 160 return '%s-%s' % (marketing_name, build_number) |
142 service_pack = platform.win32_ver()[2] or 'SP0' | 161 service_pack = platform.win32_ver()[2] or 'SP0' |
143 return '%s-%s' % (marketing_name, service_pack) | 162 return '%s-%s' % (marketing_name, service_pack) |
144 | 163 |
145 | 164 |
146 def get_startup_dir(): | 165 def get_startup_dir(): |
147 # Do not use environment variables since it wouldn't work reliably on cygwin. | 166 # Do not use environment variables since it wouldn't work reliably on cygwin. |
148 # TODO(maruel): Stop hardcoding the values and use the proper function | 167 # TODO(maruel): Stop hardcoding the values and use the proper function |
149 # described below. Postponed to a later CL since I'll have to spend quality | 168 # described below. Postponed to a later CL since I'll have to spend quality |
150 # time on Windows to ensure it works well. | 169 # time on Windows to ensure it works well. |
151 # https://msdn.microsoft.com/library/windows/desktop/bb762494.aspx | 170 # https://msdn.microsoft.com/library/windows/desktop/bb762494.aspx |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 | 404 |
386 def get_uptime(): | 405 def get_uptime(): |
387 """Return uptime for Windows 7 and later. | 406 """Return uptime for Windows 7 and later. |
388 | 407 |
389 Excludes sleep time. | 408 Excludes sleep time. |
390 """ | 409 """ |
391 val = ctypes.c_ulonglong(0) | 410 val = ctypes.c_ulonglong(0) |
392 if ctypes.windll.kernel32.QueryUnbiasedInterruptTime(ctypes.byref(val)) != 0: | 411 if ctypes.windll.kernel32.QueryUnbiasedInterruptTime(ctypes.byref(val)) != 0: |
393 return val.value / 10000000. | 412 return val.value / 10000000. |
394 return 0. | 413 return 0. |
OLD | NEW |