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