3
# Thomas Nagy, 2005-2010 (ita)
6
Utilities and platform-specific fixes
8
The portability fixes try to provide a consistent behavior of the Waf API
9
through Python versions 2.3 to 3.X and across different platforms (win32, linux, etc)
12
import os, sys, errno, traceback, inspect, re, shutil, datetime, gc
17
import waflib.extras.subprocess as subprocess
19
print("The subprocess module is missing (python2.3?):\n try calling 'waf update --files=subprocess'\n or add a copy of subprocess.py to the python libraries")
22
from collections import deque
25
"""A deque for Python 2.3 which does not have one"""
29
import _winreg as winreg
36
from waflib import Errors
39
from collections import UserDict
41
from UserDict import UserDict
44
from hashlib import md5
49
# never fail to enable fixes from another module
55
class threading(object):
57
A fake threading class for platforms lacking the threading module.
58
Use ``waf -j1`` on those platforms
67
threading.Lock = threading.Thread = Lock
69
run_old = threading.Thread.run
70
def run(*args, **kwargs):
72
run_old(*args, **kwargs)
73
except (KeyboardInterrupt, SystemExit):
76
sys.excepthook(*sys.exc_info())
77
threading.Thread.run = run
79
SIG_NIL = 'iluvcuteoverload'.encode()
80
"""Arbitrary null value for a md5 hash. This value must be changed when the hash value is replaced (size)"""
83
"""Constant representing the permissions for regular files (0644 raises a syntax error on python 3)"""
86
"""Constant representing the permissions for executable files (0755 raises a syntax error on python 3)"""
88
rot_chr = ['\\', '|', '/', '-']
89
"List of characters to use when displaying the throbber (progress bar)"
92
"Index of the current throbber character (progress bar)"
95
from collections import defaultdict
97
class defaultdict(dict):
99
defaultdict was introduced in python 2.5, so we leave it for python 2.4 and 2.3
101
def __init__(self, default_factory):
102
super(defaultdict, self).__init__()
103
self.default_factory = default_factory
104
def __getitem__(self, key):
106
return super(defaultdict, self).__getitem__(key)
108
value = self.default_factory()
112
is_win32 = sys.platform in ('win32', 'cli')
114
# we should have put this in the Logs.py file instead :-/
115
indicator = '\x1b[K%s%s%s\r'
116
if is_win32 and 'NOCOLOR' in os.environ:
117
indicator = '%s%s%s\r'
119
def readf(fname, m='r'):
121
Read an entire file into a string, in practice the wrapper
122
node.read(..) should be used instead of this method::
125
from waflib import Utils
126
txt = Utils.readf(self.path.find_node('wscript').abspath())
127
txt = ctx.path.find_node('wscript').read()
130
:param fname: Path to file
134
:return: Content of the file
143
def h_file(filename):
145
Compute a hash value for a file by using md5. This method may be replaced by
146
a faster version if necessary. The following uses the file size and the timestamp value::
149
from waflib import Utils
150
def h_file(filename):
151
st = os.stat(filename)
152
if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
154
m.update(str(st.st_mtime))
155
m.update(str(st.st_size))
158
Utils.h_file = h_file
160
:type filename: string
161
:param filename: path to the file to hash
162
:return: hash of the file contents
164
f = open(filename, 'rb')
168
filename = f.read(100000)
179
ret = binascii.hexlify(s)
180
if not isinstance(ret, str):
181
ret = ret.decode('utf-8')
185
return s.encode('hex')
188
Return the hexadecimal representation of a string
190
:param s: string to convert
196
def listdir_win32(s):
198
List the contents of a folder in a portable manner.
201
:param s: a string, which can be empty on Windows for listing the drive letters
207
# there is nothing much we can do
208
return [x + ':\\' for x in list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')]
210
dlen = 4 # length of "?:\\x00"
212
buf = ctypes.create_string_buffer(maxdrives * dlen)
213
ndrives = ctypes.windll.kernel32.GetLogicalDriveStringsA(maxdrives, ctypes.byref(buf))
214
return [ buf.raw[4*i:4*i+3].decode('ascii') for i in range(int(ndrives/dlen)) ]
216
if len(s) == 2 and s[1] == ":":
219
if not os.path.isdir(s):
221
e.errno = errno.ENOENT
224
listdir = listdir_win32
228
Convert a string, tuple or version number into an integer. The number is supposed to have at most 4 digits::
230
from waflib.Utils import num2ver
231
num2ver('1.3.2') == num2ver((1,3,2)) == num2ver((1,3,2,0))
233
:type ver: string or tuple of numbers
234
:param ver: a version number
236
if isinstance(ver, str):
237
ver = tuple(ver.split('.'))
238
if isinstance(ver, tuple):
242
ret += 256**(3 - i) * int(ver[i])
248
Extract the stack to display exceptions
250
:return: a string represening the last exception
252
exc_type, exc_value, tb = sys.exc_info()
253
exc_lines = traceback.format_exception(exc_type, exc_value, tb)
254
return ''.join(exc_lines)
258
Convert a string argument to a list by splitting on spaces, and pass
259
through a list argument unchanged::
261
from waflib.Utils import to_list
262
lst = to_list("a b c d")
264
:param sth: List or a string of items separated by spaces
266
:return: Argument converted to list
269
if isinstance(sth, str):
274
re_nl = re.compile('\r*\n', re.M)
275
def str_to_dict(txt):
277
Parse a string with key = value pairs into a dictionary::
279
from waflib import Utils
280
x = Utils.str_to_dict('''
286
:param s: String to parse
288
:return: Dictionary containing parsed key-value pairs
292
lines = re_nl.split(txt)
295
if not x or x.startswith('#') or x.find('=') < 0:
298
tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
301
def split_path(path):
302
return path.split('/')
304
def split_path_cygwin(path):
305
if path.startswith('//'):
306
ret = path.split('/')[2:]
307
ret[0] = '/' + ret[0]
309
return path.split('/')
311
re_sp = re.compile('[/\\\\]')
312
def split_path_win32(path):
313
if path.startswith('\\\\'):
314
ret = re.split(re_sp, path)[2:]
315
ret[0] = '\\' + ret[0]
317
return re.split(re_sp, path)
319
if sys.platform == 'cygwin':
320
split_path = split_path_cygwin
322
split_path = split_path_win32
324
split_path.__doc__ = """
325
Split a path by / or \\. This function is not like os.path.split
328
:param path: path to split
329
:return: list of strings
334
Ensure that a directory exists (similar to ``mkdir -p``).
337
:param dir: Path to directory
339
if not os.path.isdir(path):
343
if not os.path.isdir(path):
344
raise Errors.WafError('Cannot create the folder %r' % path, ex=e)
346
def def_attrs(cls, **kw):
348
Set default attributes on a class instance
351
:param cls: the class to update the given attributes in.
353
:param kw: dictionary of attributes names and values.
355
for k, v in kw.items():
356
if not hasattr(cls, k):
359
def quote_define_name(s):
361
Convert a string to an identifier suitable for C defines.
364
:param s: String to convert
366
:return: Identifier suitable for C defines
368
fu = re.compile("[^a-zA-Z0-9]").sub("_", s)
374
Hash lists. For tuples, using hash(tup) is much more efficient
376
:param lst: list to hash
377
:type lst: list of strings
378
:return: hash of the list
381
m.update(str(lst).encode())
388
:param fun: function to hash
390
:return: hash of the function
394
except AttributeError:
396
h = inspect.getsource(fun)
401
except AttributeError:
405
reg_subst = re.compile(r"(\\\\)|(\$\$)|\$\{([^}]+)\}")
406
def subst_vars(expr, params):
408
Replace ${VAR} with the value of VAR taken from a dict or a config set::
410
from waflib import Utils
411
s = Utils.subst_vars('${PREFIX}/bin', env)
414
:param expr: String to perform substitution on
415
:param params: Dictionary or config set to look up variable values.
423
# ConfigSet instances may contain lists
424
return params.get_flat(m.group(3))
425
except AttributeError:
426
return params[m.group(3)]
427
return reg_subst.sub(repl_var, expr)
429
def destos_to_binfmt(key):
431
Return the binary format based on the unversioned platform name.
433
:param key: platform name
435
:return: string representing the binary format
439
elif key in ('win32', 'cygwin', 'uwin', 'msys'):
443
def unversioned_sys_platform():
445
Return the unversioned platform name.
446
Some Python platform names contain versions, that depend on
447
the build environment, e.g. linux2, freebsd6, etc.
448
This returns the name without the version number. Exceptions are
449
os2 and win32, which are returned verbatim.
452
:return: Unversioned platform name
456
# The real OS is hidden under the JVM.
457
from java.lang import System
458
s = System.getProperty('os.name')
459
# see http://lopica.sourceforge.net/os.html for a list of possible values
462
elif s.startswith('Windows '):
468
elif s in ('SunOS', 'Solaris'):
472
# powerpc == darwin for our purposes
475
if s == 'win32' or s.endswith('os2') and s != 'sunos2': return s
476
return re.split('\d+$', s)[0]
480
A function that does nothing
488
Simple object for timing the execution of commands.
489
Its string representation is the current time::
491
from waflib.Utils import Timer
497
self.start_time = datetime.datetime.utcnow()
500
delta = datetime.datetime.utcnow() - self.start_time
501
days = int(delta.days)
502
hours = delta.seconds // 3600
503
minutes = (delta.seconds - hours * 3600) // 60
504
seconds = delta.seconds - hours * 3600 - minutes * 60 + float(delta.microseconds) / 1000 / 1000
507
result += '%dd' % days
509
result += '%dh' % hours
510
if days or hours or minutes:
511
result += '%dm' % minutes
512
return '%s%.3fs' % (result, seconds)
518
shutil.copy2 does not copy the file attributes on windows, so we
519
hack into the shutil module to fix the problem
522
shutil.copystat(src, dst)
523
setattr(shutil, 'copy2', copy2)
525
if os.name == 'java':
526
# Jython cannot disable the gc but they can enable it ... wtf?
530
except NotImplementedError:
531
gc.disable = gc.enable
533
def read_la_file(path):
535
Read property files, used by msvc.py
537
:param path: file to read
540
sp = re.compile(r'^([^=]+)=\'(.*)\'$')
542
for line in readf(path).splitlines():
544
_, left, right, _ = sp.split(line.strip())
552
Decorator: let a function disable the garbage collector during its execution.
553
It is used in the build context when storing/loading the build cache file (pickle)
555
:param fun: function to execute
557
:return: the return value of the function executed
566
f.__doc__ = fun.__doc__
571
Decorator: let a function cache its results, use like this::
577
:param fun: function to execute
579
:return: the return value of the function executed
589
wrap.__cache__ = cache
592
def get_registry_app_path(key, filename):
596
result = winreg.QueryValue(key, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s.exe" % filename[0])
600
if os.path.isfile(result):