Source code

Revision control

Other Tools

1
#!/usr/bin/env python
2
3
# This Source Code Form is subject to the terms of the Mozilla Public
4
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
5
# You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
# TODO: it might be a good idea of adding a system name (e.g. 'Ubuntu' for
8
# linux) to the information; I certainly wouldn't want anyone parsing this
9
# information and having behaviour depend on it
10
11
from __future__ import absolute_import, print_function
12
13
import os
14
import platform
15
import re
16
import sys
17
18
from .string_version import StringVersion
19
from ctypes.util import find_library
20
21
# keep a copy of the os module since updating globals overrides this
22
_os = os
23
24
25
class unknown(object):
26
"""marker class for unknown information"""
27
28
def __nonzero__(self):
29
return False
30
31
def __str__(self):
32
return 'UNKNOWN'
33
34
35
unknown = unknown() # singleton
36
37
38
def get_windows_version():
39
import ctypes
40
41
class OSVERSIONINFOEXW(ctypes.Structure):
42
_fields_ = [('dwOSVersionInfoSize', ctypes.c_ulong),
43
('dwMajorVersion', ctypes.c_ulong),
44
('dwMinorVersion', ctypes.c_ulong),
45
('dwBuildNumber', ctypes.c_ulong),
46
('dwPlatformId', ctypes.c_ulong),
47
('szCSDVersion', ctypes.c_wchar * 128),
48
('wServicePackMajor', ctypes.c_ushort),
49
('wServicePackMinor', ctypes.c_ushort),
50
('wSuiteMask', ctypes.c_ushort),
51
('wProductType', ctypes.c_byte),
52
('wReserved', ctypes.c_byte)]
53
54
os_version = OSVERSIONINFOEXW()
55
os_version.dwOSVersionInfoSize = ctypes.sizeof(os_version)
56
retcode = ctypes.windll.Ntdll.RtlGetVersion(ctypes.byref(os_version))
57
if retcode != 0:
58
raise OSError
59
60
return os_version.dwMajorVersion, os_version.dwMinorVersion, os_version.dwBuildNumber
61
62
63
# get system information
64
info = {'os': unknown,
65
'processor': unknown,
66
'version': unknown,
67
'os_version': unknown,
68
'bits': unknown,
69
'has_sandbox': unknown,
70
'webrender': False,
71
'automation': bool(os.environ.get("MOZ_AUTOMATION", False)),
72
}
73
(system, node, release, version, machine, processor) = platform.uname()
74
(bits, linkage) = platform.architecture()
75
76
# get os information and related data
77
if system in ["Microsoft", "Windows"]:
78
info['os'] = 'win'
79
# There is a Python bug on Windows to determine platform values
81
if "PROCESSOR_ARCHITEW6432" in os.environ:
82
processor = os.environ.get("PROCESSOR_ARCHITEW6432", processor)
83
else:
84
processor = os.environ.get('PROCESSOR_ARCHITECTURE', processor)
85
system = os.environ.get("OS", system).replace('_', ' ')
86
(major, minor, _, _, service_pack) = os.sys.getwindowsversion()
87
info['service_pack'] = service_pack
88
if major >= 6 and minor >= 2:
89
# On windows >= 8.1 the system call that getwindowsversion uses has
90
# been frozen to always return the same values. In this case we call
91
# the RtlGetVersion API directly, which still provides meaningful
92
# values, at least for now.
93
major, minor, build_number = get_windows_version()
94
version = "%d.%d.%d" % (major, minor, build_number)
95
96
os_version = "%d.%d" % (major, minor)
97
elif system.startswith(('MINGW', 'MSYS_NT')):
98
# windows/mingw python build (msys)
99
info['os'] = 'win'
100
os_version = version = unknown
101
elif system == "Linux":
102
# Attempt to use distro package to determine Linux distribution first.
103
# Failing that, fall back to use the platform method.
104
# Note that platform.linux_distribution() will be deprecated as of 3.8
105
# and this block will be removed once support for 2.7/3.5 is dropped.
106
try:
107
from distro import linux_distribution
108
except ImportError:
109
from platform import linux_distribution
110
111
output = linux_distribution()
112
(distribution, os_version, codename) = tuple(
113
str(item.title()) for item in output)
114
115
if not processor:
116
processor = machine
117
version = "%s %s" % (distribution, os_version)
118
119
# Bug in Python 2's `platform` library:
120
# It will return a triple of empty strings if the distribution is not supported.
121
# It works on Python 3. If we don't have an OS version,
122
# the unit tests fail to run.
123
if not distribution and not os_version and not codename:
124
distribution = 'lfs'
125
version = release
126
os_version = release
127
128
info['os'] = 'linux'
129
info['linux_distro'] = distribution
130
elif system in ['DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD']:
131
info['os'] = 'bsd'
132
version = os_version = sys.platform
133
elif system == "Darwin":
134
(release, versioninfo, machine) = platform.mac_ver()
135
version = "OS X %s" % release
136
versionNums = release.split('.')[:2]
137
os_version = "%s.%s" % (versionNums[0], versionNums[1])
138
info['os'] = 'mac'
139
elif sys.platform in ('solaris', 'sunos5'):
140
info['os'] = 'unix'
141
os_version = version = sys.platform
142
else:
143
os_version = version = unknown
144
145
info['version'] = version
146
info['os_version'] = StringVersion(os_version)
147
148
# processor type and bits
149
if processor in ["i386", "i686"]:
150
if bits == "32bit":
151
processor = "x86"
152
elif bits == "64bit":
153
processor = "x86_64"
154
elif processor.upper() == "AMD64":
155
bits = "64bit"
156
processor = "x86_64"
157
elif processor.upper() == "ARM64":
158
bits = "64bit"
159
processor = "aarch64"
160
elif processor == "Power Macintosh":
161
processor = "ppc"
162
bits = re.search('(\d+)bit', bits).group(1)
163
info.update({'processor': processor,
164
'bits': int(bits),
165
})
166
167
if info['os'] == 'linux':
168
import ctypes
169
import errno
170
PR_SET_SECCOMP = 22
171
SECCOMP_MODE_FILTER = 2
172
ctypes.CDLL(find_library("c"), use_errno=True).prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, 0)
173
info['has_sandbox'] = ctypes.get_errno() == errno.EFAULT
174
else:
175
info['has_sandbox'] = True
176
177
# standard value of choices, for easy inspection
178
choices = {'os': ['linux', 'bsd', 'win', 'mac', 'unix'],
179
'bits': [32, 64],
180
'processor': ['x86', 'x86_64', 'ppc']}
181
182
183
def sanitize(info):
184
"""Do some sanitization of input values, primarily
185
to handle universal Mac builds."""
186
if "processor" in info and info["processor"] == "universal-x86-x86_64":
187
# If we're running on OS X 10.6 or newer, assume 64-bit
188
if release[:4] >= "10.6": # Note this is a string comparison
189
info["processor"] = "x86_64"
190
info["bits"] = 64
191
else:
192
info["processor"] = "x86"
193
info["bits"] = 32
194
195
# method for updating information
196
197
198
def update(new_info):
199
"""
200
Update the info.
201
202
:param new_info: Either a dict containing the new info or a path/url
203
to a json file containing the new info.
204
"""
205
from six import string_types
206
if isinstance(new_info, string_types):
207
# lazy import
208
import mozfile
209
import json
210
f = mozfile.load(new_info)
211
new_info = json.loads(f.read())
212
f.close()
213
214
info.update(new_info)
215
sanitize(info)
216
globals().update(info)
217
218
# convenience data for os access
219
for os_name in choices['os']:
220
globals()['is' + os_name.title()] = info['os'] == os_name
221
# unix is special
222
if isLinux or isBsd: # noqa
223
globals()['isUnix'] = True
224
225
226
def find_and_update_from_json(*dirs, **kwargs):
227
"""Find a mozinfo.json file, load it, and update global symbol table.
228
229
This method will first check the relevant objdir directory for the
230
necessary mozinfo.json file, if the current script is being run from a
231
Mozilla objdir.
232
233
If the objdir directory did not supply the necessary data, this method
234
will then look for the required mozinfo.json file from the provided
235
tuple of directories.
236
237
If file is found, the global symbols table is updated via a helper method.
238
239
If no valid files are found, this method no-ops unless the raise_exception
240
kwargs is provided with explicit boolean value of True.
241
242
:param tuple dirs: Directories in which to look for the file.
243
:param dict kwargs: optional values:
244
raise_exception: if True, exceptions are raised.
245
False by default.
246
:returns: None: default behavior if mozinfo.json cannot be found.
247
json_path: string representation of mozinfo.json path.
248
:raises: IOError: if raise_exception is True and file is not found.
249
"""
250
# First, see if we're in an objdir
251
try:
252
from mozbuild.base import MozbuildObject, BuildEnvironmentNotFoundException
253
from mozbuild.mozconfig import MozconfigFindException
254
build = MozbuildObject.from_environment()
255
json_path = _os.path.join(build.topobjdir, "mozinfo.json")
256
if _os.path.isfile(json_path):
257
update(json_path)
258
return json_path
259
except ImportError:
260
pass
261
except (BuildEnvironmentNotFoundException, MozconfigFindException):
262
pass
263
264
for d in dirs:
265
d = _os.path.abspath(d)
266
json_path = _os.path.join(d, "mozinfo.json")
267
if _os.path.isfile(json_path):
268
update(json_path)
269
return json_path
270
271
# by default, exceptions are suppressed. Set this to True if otherwise
272
# desired.
273
if kwargs.get('raise_exception', False):
274
raise IOError('mozinfo.json could not be found.')
275
return None
276
277
278
def output_to_file(path):
279
import json
280
with open(path, 'w') as f:
281
f.write(json.dumps(info))
282
283
284
update({})
285
286
# exports
287
__all__ = list(info.keys())
288
__all__ += ['is' + os_name.title() for os_name in choices['os']]
289
__all__ += [
290
'info',
291
'unknown',
292
'main',
293
'choices',
294
'update',
295
'find_and_update_from_json',
296
'output_to_file',
297
'StringVersion',
298
]
299
300
301
def main(args=None):
302
303
# parse the command line
304
from optparse import OptionParser
305
parser = OptionParser(description=__doc__)
306
for key in choices:
307
parser.add_option('--%s' % key, dest=key,
308
action='store_true', default=False,
309
help="display choices for %s" % key)
310
options, args = parser.parse_args()
311
312
# args are JSON blobs to override info
313
if args:
314
# lazy import
315
import json
316
for arg in args:
317
if _os.path.exists(arg):
318
string = open(arg).read()
319
else:
320
string = arg
321
update(json.loads(string))
322
323
# print out choices if requested
324
flag = False
325
for key, value in options.__dict__.items():
326
if value is True:
327
print('%s choices: %s' % (key, ' '.join([str(choice)
328
for choice in choices[key]])))
329
flag = True
330
if flag:
331
return
332
333
# otherwise, print out all info
334
for key, value in info.items():
335
print('%s: %s' % (key, value))
336
337
338
if __name__ == '__main__':
339
main()