Fix yet another merge conflict
[jack2.git] / waflib / Utils.py
blobb4665c4dc2bbf64fd723206b944a42ab8a5f03da
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005-2018 (ita)
5 """
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.5 to 3.X and across different platforms (win32, linux, etc)
10 """
12 from __future__ import with_statement
14 import atexit, os, sys, errno, inspect, re, datetime, platform, base64, signal, functools, time
16 try:
17 import cPickle
18 except ImportError:
19 import pickle as cPickle
21 # leave this
22 if os.name == 'posix' and sys.version_info[0] < 3:
23 try:
24 import subprocess32 as subprocess
25 except ImportError:
26 import subprocess
27 else:
28 import subprocess
30 try:
31 TimeoutExpired = subprocess.TimeoutExpired
32 except AttributeError:
33 class TimeoutExpired(Exception):
34 pass
36 from collections import deque, defaultdict
38 try:
39 import _winreg as winreg
40 except ImportError:
41 try:
42 import winreg
43 except ImportError:
44 winreg = None
46 from waflib import Errors
48 try:
49 from hashlib import md5
50 except ImportError:
51 try:
52 from md5 import md5
53 except ImportError:
54 # never fail to enable fixes from another module
55 pass
57 try:
58 import threading
59 except ImportError:
60 if not 'JOBS' in os.environ:
61 # no threading :-(
62 os.environ['JOBS'] = '1'
64 class threading(object):
65 """
66 A fake threading class for platforms lacking the threading module.
67 Use ``waf -j1`` on those platforms
68 """
69 pass
70 class Lock(object):
71 """Fake Lock class"""
72 def acquire(self):
73 pass
74 def release(self):
75 pass
76 threading.Lock = threading.Thread = Lock
78 SIG_NIL = 'SIG_NIL_SIG_NIL_'.encode()
79 """Arbitrary null value for hashes. Modify this value according to the hash function in use"""
81 O644 = 420
82 """Constant representing the permissions for regular files (0644 raises a syntax error on python 3)"""
84 O755 = 493
85 """Constant representing the permissions for executable files (0755 raises a syntax error on python 3)"""
87 rot_chr = ['\\', '|', '/', '-']
88 "List of characters to use when displaying the throbber (progress bar)"
90 rot_idx = 0
91 "Index of the current throbber character (progress bar)"
93 class ordered_iter_dict(dict):
94 """Ordered dictionary that provides iteration from the most recently inserted keys first"""
95 def __init__(self, *k, **kw):
96 self.lst = deque()
97 dict.__init__(self, *k, **kw)
98 def clear(self):
99 dict.clear(self)
100 self.lst = deque()
101 def __setitem__(self, key, value):
102 if key in dict.keys(self):
103 self.lst.remove(key)
104 dict.__setitem__(self, key, value)
105 self.lst.append(key)
106 def __delitem__(self, key):
107 dict.__delitem__(self, key)
108 try:
109 self.lst.remove(key)
110 except ValueError:
111 pass
112 def __iter__(self):
113 return reversed(self.lst)
114 def keys(self):
115 return reversed(self.lst)
117 class lru_node(object):
119 Used by :py:class:`waflib.Utils.lru_cache`
121 __slots__ = ('next', 'prev', 'key', 'val')
122 def __init__(self):
123 self.next = self
124 self.prev = self
125 self.key = None
126 self.val = None
128 class lru_cache(object):
130 A simple least-recently used cache with lazy allocation
132 __slots__ = ('maxlen', 'table', 'head')
133 def __init__(self, maxlen=100):
134 self.maxlen = maxlen
136 Maximum amount of elements in the cache
138 self.table = {}
140 Mapping key-value
142 self.head = lru_node()
143 self.head.next = self.head
144 self.head.prev = self.head
146 def __getitem__(self, key):
147 node = self.table[key]
148 # assert(key==node.key)
149 if node is self.head:
150 return node.val
152 # detach the node found
153 node.prev.next = node.next
154 node.next.prev = node.prev
156 # replace the head
157 node.next = self.head.next
158 node.prev = self.head
159 self.head = node.next.prev = node.prev.next = node
161 return node.val
163 def __setitem__(self, key, val):
164 if key in self.table:
165 # update the value for an existing key
166 node = self.table[key]
167 node.val = val
168 self.__getitem__(key)
169 else:
170 if len(self.table) < self.maxlen:
171 # the very first item is unused until the maximum is reached
172 node = lru_node()
173 node.prev = self.head
174 node.next = self.head.next
175 node.prev.next = node.next.prev = node
176 else:
177 node = self.head = self.head.next
178 try:
179 # that's another key
180 del self.table[node.key]
181 except KeyError:
182 pass
184 node.key = key
185 node.val = val
186 self.table[key] = node
188 class lazy_generator(object):
189 def __init__(self, fun, params):
190 self.fun = fun
191 self.params = params
193 def __iter__(self):
194 return self
196 def __next__(self):
197 try:
198 it = self.it
199 except AttributeError:
200 it = self.it = self.fun(*self.params)
201 return next(it)
203 next = __next__
205 is_win32 = os.sep == '\\' or sys.platform == 'win32' # msys2
207 Whether this system is a Windows series
210 def readf(fname, m='r', encoding='latin-1'):
212 Reads an entire file into a string. See also :py:meth:`waflib.Node.Node.readf`::
214 def build(ctx):
215 from waflib import Utils
216 txt = Utils.readf(self.path.find_node('wscript').abspath())
217 txt = ctx.path.find_node('wscript').read()
219 :type fname: string
220 :param fname: Path to file
221 :type m: string
222 :param m: Open mode
223 :type encoding: string
224 :param encoding: encoding value, only used for python 3
225 :rtype: string
226 :return: Content of the file
229 if sys.hexversion > 0x3000000 and not 'b' in m:
230 m += 'b'
231 with open(fname, m) as f:
232 txt = f.read()
233 if encoding:
234 txt = txt.decode(encoding)
235 else:
236 txt = txt.decode()
237 else:
238 with open(fname, m) as f:
239 txt = f.read()
240 return txt
242 def writef(fname, data, m='w', encoding='latin-1'):
244 Writes an entire file from a string.
245 See also :py:meth:`waflib.Node.Node.writef`::
247 def build(ctx):
248 from waflib import Utils
249 txt = Utils.writef(self.path.make_node('i_like_kittens').abspath(), 'some data')
250 self.path.make_node('i_like_kittens').write('some data')
252 :type fname: string
253 :param fname: Path to file
254 :type data: string
255 :param data: The contents to write to the file
256 :type m: string
257 :param m: Open mode
258 :type encoding: string
259 :param encoding: encoding value, only used for python 3
261 if sys.hexversion > 0x3000000 and not 'b' in m:
262 data = data.encode(encoding)
263 m += 'b'
264 with open(fname, m) as f:
265 f.write(data)
267 def h_file(fname):
269 Computes a hash value for a file by using md5. Use the md5_tstamp
270 extension to get faster build hashes if necessary.
272 :type fname: string
273 :param fname: path to the file to hash
274 :return: hash of the file contents
275 :rtype: string or bytes
277 m = md5()
278 with open(fname, 'rb') as f:
279 while fname:
280 fname = f.read(200000)
281 m.update(fname)
282 return m.digest()
284 def readf_win32(f, m='r', encoding='latin-1'):
285 flags = os.O_NOINHERIT | os.O_RDONLY
286 if 'b' in m:
287 flags |= os.O_BINARY
288 if '+' in m:
289 flags |= os.O_RDWR
290 try:
291 fd = os.open(f, flags)
292 except OSError:
293 raise IOError('Cannot read from %r' % f)
295 if sys.hexversion > 0x3000000 and not 'b' in m:
296 m += 'b'
297 with os.fdopen(fd, m) as f:
298 txt = f.read()
299 if encoding:
300 txt = txt.decode(encoding)
301 else:
302 txt = txt.decode()
303 else:
304 with os.fdopen(fd, m) as f:
305 txt = f.read()
306 return txt
308 def writef_win32(f, data, m='w', encoding='latin-1'):
309 if sys.hexversion > 0x3000000 and not 'b' in m:
310 data = data.encode(encoding)
311 m += 'b'
312 flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | os.O_NOINHERIT
313 if 'b' in m:
314 flags |= os.O_BINARY
315 if '+' in m:
316 flags |= os.O_RDWR
317 try:
318 fd = os.open(f, flags)
319 except OSError:
320 raise OSError('Cannot write to %r' % f)
321 with os.fdopen(fd, m) as f:
322 f.write(data)
324 def h_file_win32(fname):
325 try:
326 fd = os.open(fname, os.O_BINARY | os.O_RDONLY | os.O_NOINHERIT)
327 except OSError:
328 raise OSError('Cannot read from %r' % fname)
329 m = md5()
330 with os.fdopen(fd, 'rb') as f:
331 while fname:
332 fname = f.read(200000)
333 m.update(fname)
334 return m.digest()
336 # always save these
337 readf_unix = readf
338 writef_unix = writef
339 h_file_unix = h_file
340 if hasattr(os, 'O_NOINHERIT') and sys.hexversion < 0x3040000:
341 # replace the default functions
342 readf = readf_win32
343 writef = writef_win32
344 h_file = h_file_win32
346 try:
347 x = ''.encode('hex')
348 except LookupError:
349 import binascii
350 def to_hex(s):
351 ret = binascii.hexlify(s)
352 if not isinstance(ret, str):
353 ret = ret.decode('utf-8')
354 return ret
355 else:
356 def to_hex(s):
357 return s.encode('hex')
359 to_hex.__doc__ = """
360 Return the hexadecimal representation of a string
362 :param s: string to convert
363 :type s: string
366 def listdir_win32(s):
368 Lists the contents of a folder in a portable manner.
369 On Win32, returns the list of drive letters: ['C:', 'X:', 'Z:'] when an empty string is given.
371 :type s: string
372 :param s: a string, which can be empty on Windows
374 if not s:
375 try:
376 import ctypes
377 except ImportError:
378 # there is nothing much we can do
379 return [x + ':\\' for x in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
380 else:
381 dlen = 4 # length of "?:\\x00"
382 maxdrives = 26
383 buf = ctypes.create_string_buffer(maxdrives * dlen)
384 ndrives = ctypes.windll.kernel32.GetLogicalDriveStringsA(maxdrives*dlen, ctypes.byref(buf))
385 return [ str(buf.raw[4*i:4*i+2].decode('ascii')) for i in range(int(ndrives/dlen)) ]
387 if len(s) == 2 and s[1] == ":":
388 s += os.sep
390 if not os.path.isdir(s):
391 e = OSError('%s is not a directory' % s)
392 e.errno = errno.ENOENT
393 raise e
394 return os.listdir(s)
396 listdir = os.listdir
397 if is_win32:
398 listdir = listdir_win32
400 def num2ver(ver):
402 Converts a string, tuple or version number into an integer. The number is supposed to have at most 4 digits::
404 from waflib.Utils import num2ver
405 num2ver('1.3.2') == num2ver((1,3,2)) == num2ver((1,3,2,0))
407 :type ver: string or tuple of numbers
408 :param ver: a version number
410 if isinstance(ver, str):
411 ver = tuple(ver.split('.'))
412 if isinstance(ver, tuple):
413 ret = 0
414 for i in range(4):
415 if i < len(ver):
416 ret += 256**(3 - i) * int(ver[i])
417 return ret
418 return ver
420 def to_list(val):
422 Converts a string argument to a list by splitting it by spaces.
423 Returns the object if not a string::
425 from waflib.Utils import to_list
426 lst = to_list('a b c d')
428 :param val: list of string or space-separated string
429 :rtype: list
430 :return: Argument converted to list
432 if isinstance(val, str):
433 return val.split()
434 else:
435 return val
437 def console_encoding():
438 try:
439 import ctypes
440 except ImportError:
441 pass
442 else:
443 try:
444 codepage = ctypes.windll.kernel32.GetConsoleCP()
445 except AttributeError:
446 pass
447 else:
448 if codepage:
449 return 'cp%d' % codepage
450 return sys.stdout.encoding or ('cp1252' if is_win32 else 'latin-1')
452 def split_path_unix(path):
453 return path.split('/')
455 def split_path_cygwin(path):
456 if path.startswith('//'):
457 ret = path.split('/')[2:]
458 ret[0] = '/' + ret[0]
459 return ret
460 return path.split('/')
462 re_sp = re.compile('[/\\\\]+')
463 def split_path_win32(path):
464 if path.startswith('\\\\'):
465 ret = re_sp.split(path)[1:]
466 ret[0] = '\\\\' + ret[0]
467 if ret[0] == '\\\\?':
468 return ret[1:]
469 return ret
470 return re_sp.split(path)
472 msysroot = None
473 def split_path_msys(path):
474 if path.startswith(('/', '\\')) and not path.startswith(('//', '\\\\')):
475 # msys paths can be in the form /usr/bin
476 global msysroot
477 if not msysroot:
478 # msys has python 2.7 or 3, so we can use this
479 msysroot = subprocess.check_output(['cygpath', '-w', '/']).decode(sys.stdout.encoding or 'latin-1')
480 msysroot = msysroot.strip()
481 path = os.path.normpath(msysroot + os.sep + path)
482 return split_path_win32(path)
484 if sys.platform == 'cygwin':
485 split_path = split_path_cygwin
486 elif is_win32:
487 if os.environ.get('MSYSTEM'):
488 split_path = split_path_msys
489 else:
490 split_path = split_path_win32
491 else:
492 split_path = split_path_unix
494 split_path.__doc__ = """
495 Splits a path by / or \\; do not confuse this function with with ``os.path.split``
497 :type path: string
498 :param path: path to split
499 :return: list of string
502 def check_dir(path):
504 Ensures that a directory exists (similar to ``mkdir -p``).
506 :type path: string
507 :param path: Path to directory
508 :raises: :py:class:`waflib.Errors.WafError` if the folder cannot be added.
510 if not os.path.isdir(path):
511 try:
512 os.makedirs(path)
513 except OSError as e:
514 if not os.path.isdir(path):
515 raise Errors.WafError('Cannot create the folder %r' % path, ex=e)
517 def check_exe(name, env=None):
519 Ensures that a program exists
521 :type name: string
522 :param name: path to the program
523 :param env: configuration object
524 :type env: :py:class:`waflib.ConfigSet.ConfigSet`
525 :return: path of the program or None
526 :raises: :py:class:`waflib.Errors.WafError` if the folder cannot be added.
528 if not name:
529 raise ValueError('Cannot execute an empty string!')
530 def is_exe(fpath):
531 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
533 fpath, fname = os.path.split(name)
534 if fpath and is_exe(name):
535 return os.path.abspath(name)
536 else:
537 env = env or os.environ
538 for path in env['PATH'].split(os.pathsep):
539 path = path.strip('"')
540 exe_file = os.path.join(path, name)
541 if is_exe(exe_file):
542 return os.path.abspath(exe_file)
543 return None
545 def def_attrs(cls, **kw):
547 Sets default attributes on a class instance
549 :type cls: class
550 :param cls: the class to update the given attributes in.
551 :type kw: dict
552 :param kw: dictionary of attributes names and values.
554 for k, v in kw.items():
555 if not hasattr(cls, k):
556 setattr(cls, k, v)
558 def quote_define_name(s):
560 Converts a string into an identifier suitable for C defines.
562 :type s: string
563 :param s: String to convert
564 :rtype: string
565 :return: Identifier suitable for C defines
567 fu = re.sub('[^a-zA-Z0-9]', '_', s)
568 fu = re.sub('_+', '_', fu)
569 fu = fu.upper()
570 return fu
572 re_sh = re.compile('\\s|\'|"')
574 Regexp used for shell_escape below
577 def shell_escape(cmd):
579 Escapes a command:
580 ['ls', '-l', 'arg space'] -> ls -l 'arg space'
582 if isinstance(cmd, str):
583 return cmd
584 return ' '.join(repr(x) if re_sh.search(x) else x for x in cmd)
586 def h_list(lst):
588 Hashes lists of ordered data.
590 Using hash(tup) for tuples would be much more efficient,
591 but Python now enforces hash randomization
593 :param lst: list to hash
594 :type lst: list of strings
595 :return: hash of the list
597 return md5(repr(lst).encode()).digest()
599 def h_fun(fun):
601 Hash functions
603 :param fun: function to hash
604 :type fun: function
605 :return: hash of the function
606 :rtype: string or bytes
608 try:
609 return fun.code
610 except AttributeError:
611 if isinstance(fun, functools.partial):
612 code = list(fun.args)
613 # The method items() provides a sequence of tuples where the first element
614 # represents an optional argument of the partial function application
616 # The sorting result outcome will be consistent because:
617 # 1. tuples are compared in order of their elements
618 # 2. optional argument namess are unique
619 code.extend(sorted(fun.keywords.items()))
620 code.append(h_fun(fun.func))
621 fun.code = h_list(code)
622 return fun.code
623 try:
624 h = inspect.getsource(fun)
625 except EnvironmentError:
626 h = 'nocode'
627 try:
628 fun.code = h
629 except AttributeError:
630 pass
631 return h
633 def h_cmd(ins):
635 Hashes objects recursively
637 :param ins: input object
638 :type ins: string or list or tuple or function
639 :rtype: string or bytes
641 # this function is not meant to be particularly fast
642 if isinstance(ins, str):
643 # a command is either a string
644 ret = ins
645 elif isinstance(ins, list) or isinstance(ins, tuple):
646 # or a list of functions/strings
647 ret = str([h_cmd(x) for x in ins])
648 else:
649 # or just a python function
650 ret = str(h_fun(ins))
651 if sys.hexversion > 0x3000000:
652 ret = ret.encode('latin-1', 'xmlcharrefreplace')
653 return ret
655 reg_subst = re.compile(r"(\\\\)|(\$\$)|\$\{([^}]+)\}")
656 def subst_vars(expr, params):
658 Replaces ${VAR} with the value of VAR taken from a dict or a config set::
660 from waflib import Utils
661 s = Utils.subst_vars('${PREFIX}/bin', env)
663 :type expr: string
664 :param expr: String to perform substitution on
665 :param params: Dictionary or config set to look up variable values.
667 def repl_var(m):
668 if m.group(1):
669 return '\\'
670 if m.group(2):
671 return '$'
672 try:
673 # ConfigSet instances may contain lists
674 return params.get_flat(m.group(3))
675 except AttributeError:
676 return params[m.group(3)]
677 # if you get a TypeError, it means that 'expr' is not a string...
678 # Utils.subst_vars(None, env) will not work
679 return reg_subst.sub(repl_var, expr)
681 def destos_to_binfmt(key):
683 Returns the binary format based on the unversioned platform name,
684 and defaults to ``elf`` if nothing is found.
686 :param key: platform name
687 :type key: string
688 :return: string representing the binary format
690 if key == 'darwin':
691 return 'mac-o'
692 elif key in ('win32', 'cygwin', 'uwin', 'msys'):
693 return 'pe'
694 return 'elf'
696 def unversioned_sys_platform():
698 Returns the unversioned platform name.
699 Some Python platform names contain versions, that depend on
700 the build environment, e.g. linux2, freebsd6, etc.
701 This returns the name without the version number. Exceptions are
702 os2 and win32, which are returned verbatim.
704 :rtype: string
705 :return: Unversioned platform name
707 s = sys.platform
708 if s.startswith('java'):
709 # The real OS is hidden under the JVM.
710 from java.lang import System
711 s = System.getProperty('os.name')
712 # see http://lopica.sourceforge.net/os.html for a list of possible values
713 if s == 'Mac OS X':
714 return 'darwin'
715 elif s.startswith('Windows '):
716 return 'win32'
717 elif s == 'OS/2':
718 return 'os2'
719 elif s == 'HP-UX':
720 return 'hp-ux'
721 elif s in ('SunOS', 'Solaris'):
722 return 'sunos'
723 else: s = s.lower()
725 # powerpc == darwin for our purposes
726 if s == 'powerpc':
727 return 'darwin'
728 if s == 'win32' or s == 'os2':
729 return s
730 if s == 'cli' and os.name == 'nt':
731 # ironpython is only on windows as far as we know
732 return 'win32'
733 return re.split('\d+$', s)[0]
735 def nada(*k, **kw):
737 Does nothing
739 :return: None
741 pass
743 class Timer(object):
745 Simple object for timing the execution of commands.
746 Its string representation is the duration::
748 from waflib.Utils import Timer
749 timer = Timer()
750 a_few_operations()
751 s = str(timer)
753 def __init__(self):
754 self.start_time = self.now()
756 def __str__(self):
757 delta = self.now() - self.start_time
758 if not isinstance(delta, datetime.timedelta):
759 delta = datetime.timedelta(seconds=delta)
760 days = delta.days
761 hours, rem = divmod(delta.seconds, 3600)
762 minutes, seconds = divmod(rem, 60)
763 seconds += delta.microseconds * 1e-6
764 result = ''
765 if days:
766 result += '%dd' % days
767 if days or hours:
768 result += '%dh' % hours
769 if days or hours or minutes:
770 result += '%dm' % minutes
771 return '%s%.3fs' % (result, seconds)
773 def now(self):
774 return datetime.datetime.utcnow()
776 if hasattr(time, 'perf_counter'):
777 def now(self):
778 return time.perf_counter()
780 def read_la_file(path):
782 Reads property files, used by msvc.py
784 :param path: file to read
785 :type path: string
787 sp = re.compile(r'^([^=]+)=\'(.*)\'$')
788 dc = {}
789 for line in readf(path).splitlines():
790 try:
791 _, left, right, _ = sp.split(line.strip())
792 dc[left] = right
793 except ValueError:
794 pass
795 return dc
797 def run_once(fun):
799 Decorator: let a function cache its results, use like this::
801 @run_once
802 def foo(k):
803 return 345*2343
805 .. note:: in practice this can cause memory leaks, prefer a :py:class:`waflib.Utils.lru_cache`
807 :param fun: function to execute
808 :type fun: function
809 :return: the return value of the function executed
811 cache = {}
812 def wrap(*k):
813 try:
814 return cache[k]
815 except KeyError:
816 ret = fun(*k)
817 cache[k] = ret
818 return ret
819 wrap.__cache__ = cache
820 wrap.__name__ = fun.__name__
821 return wrap
823 def get_registry_app_path(key, filename):
825 Returns the value of a registry key for an executable
827 :type key: string
828 :type filename: list of string
830 if not winreg:
831 return None
832 try:
833 result = winreg.QueryValue(key, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s.exe" % filename[0])
834 except OSError:
835 pass
836 else:
837 if os.path.isfile(result):
838 return result
840 def lib64():
842 Guess the default ``/usr/lib`` extension for 64-bit applications
844 :return: '64' or ''
845 :rtype: string
847 # default settings for /usr/lib
848 if os.sep == '/':
849 if platform.architecture()[0] == '64bit':
850 if os.path.exists('/usr/lib64') and not os.path.exists('/usr/lib32'):
851 return '64'
852 return ''
854 def sane_path(p):
855 # private function for the time being!
856 return os.path.abspath(os.path.expanduser(p))
858 process_pool = []
860 List of processes started to execute sub-process commands
863 def get_process():
865 Returns a process object that can execute commands as sub-processes
867 :rtype: subprocess.Popen
869 try:
870 return process_pool.pop()
871 except IndexError:
872 filepath = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'processor.py'
873 cmd = [sys.executable, '-c', readf(filepath)]
874 return subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0)
876 def run_prefork_process(cmd, kwargs, cargs):
878 Delegates process execution to a pre-forked process instance.
880 if not 'env' in kwargs:
881 kwargs['env'] = dict(os.environ)
882 try:
883 obj = base64.b64encode(cPickle.dumps([cmd, kwargs, cargs]))
884 except (TypeError, AttributeError):
885 return run_regular_process(cmd, kwargs, cargs)
887 proc = get_process()
888 if not proc:
889 return run_regular_process(cmd, kwargs, cargs)
891 proc.stdin.write(obj)
892 proc.stdin.write('\n'.encode())
893 proc.stdin.flush()
894 obj = proc.stdout.readline()
895 if not obj:
896 raise OSError('Preforked sub-process %r died' % proc.pid)
898 process_pool.append(proc)
899 lst = cPickle.loads(base64.b64decode(obj))
900 # Jython wrapper failures (bash/execvp)
901 assert len(lst) == 5
902 ret, out, err, ex, trace = lst
903 if ex:
904 if ex == 'OSError':
905 raise OSError(trace)
906 elif ex == 'ValueError':
907 raise ValueError(trace)
908 elif ex == 'TimeoutExpired':
909 exc = TimeoutExpired(cmd, timeout=cargs['timeout'], output=out)
910 exc.stderr = err
911 raise exc
912 else:
913 raise Exception(trace)
914 return ret, out, err
916 def lchown(path, user=-1, group=-1):
918 Change the owner/group of a path, raises an OSError if the
919 ownership change fails.
921 :param user: user to change
922 :type user: int or str
923 :param group: group to change
924 :type group: int or str
926 if isinstance(user, str):
927 import pwd
928 entry = pwd.getpwnam(user)
929 if not entry:
930 raise OSError('Unknown user %r' % user)
931 user = entry[2]
932 if isinstance(group, str):
933 import grp
934 entry = grp.getgrnam(group)
935 if not entry:
936 raise OSError('Unknown group %r' % group)
937 group = entry[2]
938 return os.lchown(path, user, group)
940 def run_regular_process(cmd, kwargs, cargs={}):
942 Executes a subprocess command by using subprocess.Popen
944 proc = subprocess.Popen(cmd, **kwargs)
945 if kwargs.get('stdout') or kwargs.get('stderr'):
946 try:
947 out, err = proc.communicate(**cargs)
948 except TimeoutExpired:
949 if kwargs.get('start_new_session') and hasattr(os, 'killpg'):
950 os.killpg(proc.pid, signal.SIGKILL)
951 else:
952 proc.kill()
953 out, err = proc.communicate()
954 exc = TimeoutExpired(proc.args, timeout=cargs['timeout'], output=out)
955 exc.stderr = err
956 raise exc
957 status = proc.returncode
958 else:
959 out, err = (None, None)
960 try:
961 status = proc.wait(**cargs)
962 except TimeoutExpired as e:
963 if kwargs.get('start_new_session') and hasattr(os, 'killpg'):
964 os.killpg(proc.pid, signal.SIGKILL)
965 else:
966 proc.kill()
967 proc.wait()
968 raise e
969 return status, out, err
971 def run_process(cmd, kwargs, cargs={}):
973 Executes a subprocess by using a pre-forked process when possible
974 or falling back to subprocess.Popen. See :py:func:`waflib.Utils.run_prefork_process`
975 and :py:func:`waflib.Utils.run_regular_process`
977 if kwargs.get('stdout') and kwargs.get('stderr'):
978 return run_prefork_process(cmd, kwargs, cargs)
979 else:
980 return run_regular_process(cmd, kwargs, cargs)
982 def alloc_process_pool(n, force=False):
984 Allocates an amount of processes to the default pool so its size is at least *n*.
985 It is useful to call this function early so that the pre-forked
986 processes use as little memory as possible.
988 :param n: pool size
989 :type n: integer
990 :param force: if True then *n* more processes are added to the existing pool
991 :type force: bool
993 # mandatory on python2, unnecessary on python >= 3.2
994 global run_process, get_process, alloc_process_pool
995 if not force:
996 n = max(n - len(process_pool), 0)
997 try:
998 lst = [get_process() for x in range(n)]
999 except OSError:
1000 run_process = run_regular_process
1001 get_process = alloc_process_pool = nada
1002 else:
1003 for x in lst:
1004 process_pool.append(x)
1006 def atexit_pool():
1007 for k in process_pool:
1008 try:
1009 os.kill(k.pid, 9)
1010 except OSError:
1011 pass
1012 else:
1013 k.wait()
1014 # see #1889
1015 if (sys.hexversion<0x207000f and not is_win32) or sys.hexversion>=0x306000f:
1016 atexit.register(atexit_pool)
1018 if os.environ.get('WAF_NO_PREFORK') or sys.platform == 'cli' or not sys.executable:
1019 run_process = run_regular_process
1020 get_process = alloc_process_pool = nada