ctdb-scripts: Support storing statd-callout state in cluster filesystem
[samba4-gss.git] / third_party / waf / waflib / Utils.py
blobea0f7a9db8813f8c65279330108bba988ca11073
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, shlex
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 hashlib import sha1 as md5
53 except ImportError:
54 # never fail to enable potential fixes from another module
55 pass
56 else:
57 try:
58 md5().digest()
59 except ValueError:
60 # Fips? #2213
61 from hashlib import sha1 as md5
63 try:
64 import threading
65 except ImportError:
66 if not 'JOBS' in os.environ:
67 # no threading :-(
68 os.environ['JOBS'] = '1'
70 class threading(object):
71 """
72 A fake threading class for platforms lacking the threading module.
73 Use ``waf -j1`` on those platforms
74 """
75 pass
76 class Lock(object):
77 """Fake Lock class"""
78 def acquire(self):
79 pass
80 def release(self):
81 pass
82 threading.Lock = threading.Thread = Lock
84 SIG_NIL = 'SIG_NIL_SIG_NIL_'.encode()
85 """Arbitrary null value for hashes. Modify this value according to the hash function in use"""
87 O644 = 420
88 """Constant representing the permissions for regular files (0644 raises a syntax error on python 3)"""
90 O755 = 493
91 """Constant representing the permissions for executable files (0755 raises a syntax error on python 3)"""
93 rot_chr = ['\\', '|', '/', '-']
94 "List of characters to use when displaying the throbber (progress bar)"
96 rot_idx = 0
97 "Index of the current throbber character (progress bar)"
99 class ordered_iter_dict(dict):
100 """Ordered dictionary that provides iteration from the most recently inserted keys first"""
101 def __init__(self, *k, **kw):
102 self.lst = deque()
103 dict.__init__(self, *k, **kw)
104 def clear(self):
105 dict.clear(self)
106 self.lst = deque()
107 def __setitem__(self, key, value):
108 if key in dict.keys(self):
109 self.lst.remove(key)
110 dict.__setitem__(self, key, value)
111 self.lst.append(key)
112 def __delitem__(self, key):
113 dict.__delitem__(self, key)
114 try:
115 self.lst.remove(key)
116 except ValueError:
117 pass
118 def __iter__(self):
119 return reversed(self.lst)
120 def keys(self):
121 return reversed(self.lst)
123 class lru_node(object):
125 Used by :py:class:`waflib.Utils.lru_cache`
127 __slots__ = ('next', 'prev', 'key', 'val')
128 def __init__(self):
129 self.next = self
130 self.prev = self
131 self.key = None
132 self.val = None
134 class lru_cache(object):
136 A simple least-recently used cache with lazy allocation
138 __slots__ = ('maxlen', 'table', 'head')
139 def __init__(self, maxlen=100):
140 self.maxlen = maxlen
142 Maximum amount of elements in the cache
144 self.table = {}
146 Mapping key-value
148 self.head = lru_node()
149 self.head.next = self.head
150 self.head.prev = self.head
152 def __getitem__(self, key):
153 node = self.table[key]
154 # assert(key==node.key)
155 if node is self.head:
156 return node.val
158 # detach the node found
159 node.prev.next = node.next
160 node.next.prev = node.prev
162 # replace the head
163 node.next = self.head.next
164 node.prev = self.head
165 self.head = node.next.prev = node.prev.next = node
167 return node.val
169 def __setitem__(self, key, val):
170 if key in self.table:
171 # update the value for an existing key
172 node = self.table[key]
173 node.val = val
174 self.__getitem__(key)
175 else:
176 if len(self.table) < self.maxlen:
177 # the very first item is unused until the maximum is reached
178 node = lru_node()
179 node.prev = self.head
180 node.next = self.head.next
181 node.prev.next = node.next.prev = node
182 else:
183 node = self.head = self.head.next
184 try:
185 # that's another key
186 del self.table[node.key]
187 except KeyError:
188 pass
190 node.key = key
191 node.val = val
192 self.table[key] = node
194 class lazy_generator(object):
195 def __init__(self, fun, params):
196 self.fun = fun
197 self.params = params
199 def __iter__(self):
200 return self
202 def __next__(self):
203 try:
204 it = self.it
205 except AttributeError:
206 it = self.it = self.fun(*self.params)
207 return next(it)
209 next = __next__
211 is_win32 = os.sep == '\\' or sys.platform == 'win32' or os.name == 'nt' # msys2
213 Whether this system is a Windows series
216 def readf(fname, m='r', encoding='latin-1'):
218 Reads an entire file into a string. See also :py:meth:`waflib.Node.Node.readf`::
220 def build(ctx):
221 from waflib import Utils
222 txt = Utils.readf(self.path.find_node('wscript').abspath())
223 txt = ctx.path.find_node('wscript').read()
225 :type fname: string
226 :param fname: Path to file
227 :type m: string
228 :param m: Open mode
229 :type encoding: string
230 :param encoding: encoding value, only used for python 3
231 :rtype: string
232 :return: Content of the file
235 if sys.hexversion > 0x3000000 and not 'b' in m:
236 m += 'b'
237 with open(fname, m) as f:
238 txt = f.read()
239 if encoding:
240 txt = txt.decode(encoding)
241 else:
242 txt = txt.decode()
243 else:
244 with open(fname, m) as f:
245 txt = f.read()
246 return txt
248 def writef(fname, data, m='w', encoding='latin-1'):
250 Writes an entire file from a string.
251 See also :py:meth:`waflib.Node.Node.writef`::
253 def build(ctx):
254 from waflib import Utils
255 txt = Utils.writef(self.path.make_node('i_like_kittens').abspath(), 'some data')
256 self.path.make_node('i_like_kittens').write('some data')
258 :type fname: string
259 :param fname: Path to file
260 :type data: string
261 :param data: The contents to write to the file
262 :type m: string
263 :param m: Open mode
264 :type encoding: string
265 :param encoding: encoding value, only used for python 3
267 if sys.hexversion > 0x3000000 and not 'b' in m:
268 data = data.encode(encoding)
269 m += 'b'
270 with open(fname, m) as f:
271 f.write(data)
273 def h_file(fname):
275 Computes a hash value for a file by using md5. Use the md5_tstamp
276 extension to get faster build hashes if necessary.
278 :type fname: string
279 :param fname: path to the file to hash
280 :return: hash of the file contents
281 :rtype: string or bytes
283 m = md5()
284 with open(fname, 'rb') as f:
285 while fname:
286 fname = f.read(200000)
287 m.update(fname)
288 return m.digest()
290 def readf_win32(f, m='r', encoding='latin-1'):
291 flags = os.O_NOINHERIT | os.O_RDONLY
292 if 'b' in m:
293 flags |= os.O_BINARY
294 if '+' in m:
295 flags |= os.O_RDWR
296 try:
297 fd = os.open(f, flags)
298 except OSError:
299 raise IOError('Cannot read from %r' % f)
301 if sys.hexversion > 0x3000000 and not 'b' in m:
302 m += 'b'
303 with os.fdopen(fd, m) as f:
304 txt = f.read()
305 if encoding:
306 txt = txt.decode(encoding)
307 else:
308 txt = txt.decode()
309 else:
310 with os.fdopen(fd, m) as f:
311 txt = f.read()
312 return txt
314 def writef_win32(f, data, m='w', encoding='latin-1'):
315 if sys.hexversion > 0x3000000 and not 'b' in m:
316 data = data.encode(encoding)
317 m += 'b'
318 flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | os.O_NOINHERIT
319 if 'b' in m:
320 flags |= os.O_BINARY
321 if '+' in m:
322 flags |= os.O_RDWR
323 try:
324 fd = os.open(f, flags)
325 except OSError:
326 raise OSError('Cannot write to %r' % f)
327 with os.fdopen(fd, m) as f:
328 f.write(data)
330 def h_file_win32(fname):
331 try:
332 fd = os.open(fname, os.O_BINARY | os.O_RDONLY | os.O_NOINHERIT)
333 except OSError:
334 raise OSError('Cannot read from %r' % fname)
335 m = md5()
336 with os.fdopen(fd, 'rb') as f:
337 while fname:
338 fname = f.read(200000)
339 m.update(fname)
340 return m.digest()
342 # always save these
343 readf_unix = readf
344 writef_unix = writef
345 h_file_unix = h_file
346 if hasattr(os, 'O_NOINHERIT') and sys.hexversion < 0x3040000:
347 # replace the default functions
348 readf = readf_win32
349 writef = writef_win32
350 h_file = h_file_win32
352 try:
353 x = ''.encode('hex')
354 except LookupError:
355 import binascii
356 def to_hex(s):
357 ret = binascii.hexlify(s)
358 if not isinstance(ret, str):
359 ret = ret.decode('utf-8')
360 return ret
361 else:
362 def to_hex(s):
363 return s.encode('hex')
365 to_hex.__doc__ = """
366 Return the hexadecimal representation of a string
368 :param s: string to convert
369 :type s: string
372 def listdir_win32(s):
374 Lists the contents of a folder in a portable manner.
375 On Win32, returns the list of drive letters: ['C:', 'X:', 'Z:'] when an empty string is given.
377 :type s: string
378 :param s: a string, which can be empty on Windows
380 if not s:
381 try:
382 import ctypes
383 except ImportError:
384 # there is nothing much we can do
385 return [x + ':\\' for x in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
386 else:
387 dlen = 4 # length of "?:\\x00"
388 maxdrives = 26
389 buf = ctypes.create_string_buffer(maxdrives * dlen)
390 ndrives = ctypes.windll.kernel32.GetLogicalDriveStringsA(maxdrives*dlen, ctypes.byref(buf))
391 return [ str(buf.raw[4*i:4*i+2].decode('ascii')) for i in range(int(ndrives/dlen)) ]
393 if len(s) == 2 and s[1] == ":":
394 s += os.sep
396 if not os.path.isdir(s):
397 e = OSError('%s is not a directory' % s)
398 e.errno = errno.ENOENT
399 raise e
400 return os.listdir(s)
402 listdir = os.listdir
403 if is_win32:
404 listdir = listdir_win32
406 def num2ver(ver):
408 Converts a string, tuple or version number into an integer. The number is supposed to have at most 4 digits::
410 from waflib.Utils import num2ver
411 num2ver('1.3.2') == num2ver((1,3,2)) == num2ver((1,3,2,0))
413 :type ver: string or tuple of numbers
414 :param ver: a version number
416 if isinstance(ver, str):
417 ver = tuple(ver.split('.'))
418 if isinstance(ver, tuple):
419 ret = 0
420 for i in range(4):
421 if i < len(ver):
422 ret += 256**(3 - i) * int(ver[i])
423 return ret
424 return ver
426 def to_list(val):
428 Converts a string argument to a list by splitting it by spaces.
429 Returns the object if not a string::
431 from waflib.Utils import to_list
432 lst = to_list('a b c d')
434 :param val: list of string or space-separated string
435 :rtype: list
436 :return: Argument converted to list
438 if isinstance(val, str):
439 return val.split()
440 else:
441 return val
443 def console_encoding():
444 try:
445 import ctypes
446 except ImportError:
447 pass
448 else:
449 try:
450 codepage = ctypes.windll.kernel32.GetConsoleCP()
451 except AttributeError:
452 pass
453 else:
454 if codepage:
455 if 65001 == codepage and sys.version_info < (3, 3):
456 return 'utf-8'
457 return 'cp%d' % codepage
458 return sys.stdout.encoding or ('cp1252' if is_win32 else 'latin-1')
460 def split_path_unix(path):
461 return path.split('/')
463 def split_path_cygwin(path):
464 if path.startswith('//'):
465 ret = path.split('/')[2:]
466 ret[0] = '/' + ret[0]
467 return ret
468 return path.split('/')
470 re_sp = re.compile('[/\\\\]+')
471 def split_path_win32(path):
472 if path.startswith('\\\\'):
473 ret = re_sp.split(path)[1:]
474 ret[0] = '\\\\' + ret[0]
475 if ret[0] == '\\\\?':
476 return ret[1:]
477 return ret
478 return re_sp.split(path)
480 msysroot = None
481 def split_path_msys(path):
482 if path.startswith(('/', '\\')) and not path.startswith(('//', '\\\\')):
483 # msys paths can be in the form /usr/bin
484 global msysroot
485 if not msysroot:
486 # msys has python 2.7 or 3, so we can use this
487 msysroot = subprocess.check_output(['cygpath', '-w', '/']).decode(sys.stdout.encoding or 'latin-1')
488 msysroot = msysroot.strip()
489 path = os.path.normpath(msysroot + os.sep + path)
490 return split_path_win32(path)
492 if sys.platform == 'cygwin':
493 split_path = split_path_cygwin
494 elif is_win32:
495 # Consider this an MSYSTEM environment if $MSYSTEM is set and python
496 # reports is executable from a unix like path on a windows host.
497 if os.environ.get('MSYSTEM') and sys.executable.startswith('/'):
498 split_path = split_path_msys
499 else:
500 split_path = split_path_win32
501 else:
502 split_path = split_path_unix
504 split_path.__doc__ = """
505 Splits a path by / or \\; do not confuse this function with with ``os.path.split``
507 :type path: string
508 :param path: path to split
509 :return: list of string
512 def check_dir(path):
514 Ensures that a directory exists (similar to ``mkdir -p``).
516 :type path: string
517 :param path: Path to directory
518 :raises: :py:class:`waflib.Errors.WafError` if the folder cannot be added.
520 if not os.path.isdir(path):
521 try:
522 os.makedirs(path)
523 except OSError as e:
524 if not os.path.isdir(path):
525 raise Errors.WafError('Cannot create the folder %r' % path, ex=e)
527 def check_exe(name, env=None):
529 Ensures that a program exists
531 :type name: string
532 :param name: path to the program
533 :param env: configuration object
534 :type env: :py:class:`waflib.ConfigSet.ConfigSet`
535 :return: path of the program or None
536 :raises: :py:class:`waflib.Errors.WafError` if the folder cannot be added.
538 if not name:
539 raise ValueError('Cannot execute an empty string!')
540 def is_exe(fpath):
541 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
543 fpath, fname = os.path.split(name)
544 if fpath and is_exe(name):
545 return os.path.abspath(name)
546 else:
547 env = env or os.environ
548 for path in env['PATH'].split(os.pathsep):
549 path = path.strip('"')
550 exe_file = os.path.join(path, name)
551 if is_exe(exe_file):
552 return os.path.abspath(exe_file)
553 return None
555 def def_attrs(cls, **kw):
557 Sets default attributes on a class instance
559 :type cls: class
560 :param cls: the class to update the given attributes in.
561 :type kw: dict
562 :param kw: dictionary of attributes names and values.
564 for k, v in kw.items():
565 if not hasattr(cls, k):
566 setattr(cls, k, v)
568 def quote_define_name(s):
570 Converts a string into an identifier suitable for C defines.
572 :type s: string
573 :param s: String to convert
574 :rtype: string
575 :return: Identifier suitable for C defines
577 fu = re.sub('[^a-zA-Z0-9]', '_', s)
578 fu = re.sub('_+', '_', fu)
579 fu = fu.upper()
580 return fu
582 # shlex.quote didn't exist until python 3.3. Prior to that it was a non-documented
583 # function in pipes.
584 try:
585 shell_quote = shlex.quote
586 except AttributeError:
587 import pipes
588 shell_quote = pipes.quote
590 def shell_escape(cmd):
592 Escapes a command:
593 ['ls', '-l', 'arg space'] -> ls -l 'arg space'
595 if isinstance(cmd, str):
596 return cmd
597 return ' '.join(shell_quote(x) for x in cmd)
599 def h_list(lst):
601 Hashes lists of ordered data.
603 Using hash(tup) for tuples would be much more efficient,
604 but Python now enforces hash randomization
606 :param lst: list to hash
607 :type lst: list of strings
608 :return: hash of the list
610 return md5(repr(lst).encode()).digest()
612 if sys.hexversion < 0x3000000:
613 def h_list_python2(lst):
614 return md5(repr(lst)).digest()
615 h_list_python2.__doc__ = h_list.__doc__
616 h_list = h_list_python2
618 def h_fun(fun):
620 Hash functions
622 :param fun: function to hash
623 :type fun: function
624 :return: hash of the function
625 :rtype: string or bytes
627 try:
628 return fun.code
629 except AttributeError:
630 if isinstance(fun, functools.partial):
631 code = list(fun.args)
632 # The method items() provides a sequence of tuples where the first element
633 # represents an optional argument of the partial function application
635 # The sorting result outcome will be consistent because:
636 # 1. tuples are compared in order of their elements
637 # 2. optional argument namess are unique
638 code.extend(sorted(fun.keywords.items()))
639 code.append(h_fun(fun.func))
640 fun.code = h_list(code)
641 return fun.code
642 try:
643 h = inspect.getsource(fun)
644 except EnvironmentError:
645 h = 'nocode'
646 try:
647 fun.code = h
648 except AttributeError:
649 pass
650 return h
652 def h_cmd(ins):
654 Hashes objects recursively
656 :param ins: input object
657 :type ins: string or list or tuple or function
658 :rtype: string or bytes
660 # this function is not meant to be particularly fast
661 if isinstance(ins, str):
662 # a command is either a string
663 ret = ins
664 elif isinstance(ins, list) or isinstance(ins, tuple):
665 # or a list of functions/strings
666 ret = str([h_cmd(x) for x in ins])
667 else:
668 # or just a python function
669 ret = str(h_fun(ins))
670 if sys.hexversion > 0x3000000:
671 ret = ret.encode('latin-1', 'xmlcharrefreplace')
672 return ret
674 reg_subst = re.compile(r"(\\\\)|(\$\$)|\$\{([^}]+)\}")
675 def subst_vars(expr, params):
677 Replaces ${VAR} with the value of VAR taken from a dict or a config set::
679 from waflib import Utils
680 s = Utils.subst_vars('${PREFIX}/bin', env)
682 :type expr: string
683 :param expr: String to perform substitution on
684 :param params: Dictionary or config set to look up variable values.
686 def repl_var(m):
687 if m.group(1):
688 return '\\'
689 if m.group(2):
690 return '$'
691 try:
692 # ConfigSet instances may contain lists
693 return params.get_flat(m.group(3))
694 except AttributeError:
695 return params[m.group(3)]
696 # if you get a TypeError, it means that 'expr' is not a string...
697 # Utils.subst_vars(None, env) will not work
698 return reg_subst.sub(repl_var, expr)
700 def destos_to_binfmt(key):
702 Returns the binary format based on the unversioned platform name,
703 and defaults to ``elf`` if nothing is found.
705 :param key: platform name
706 :type key: string
707 :return: string representing the binary format
709 if key == 'darwin':
710 return 'mac-o'
711 elif key in ('win32', 'cygwin', 'uwin', 'msys'):
712 return 'pe'
713 return 'elf'
715 def unversioned_sys_platform():
717 Returns the unversioned platform name.
718 Some Python platform names contain versions, that depend on
719 the build environment, e.g. linux2, freebsd6, etc.
720 This returns the name without the version number. Exceptions are
721 os2 and win32, which are returned verbatim.
723 :rtype: string
724 :return: Unversioned platform name
726 s = sys.platform
727 if s.startswith('java'):
728 # The real OS is hidden under the JVM.
729 from java.lang import System
730 s = System.getProperty('os.name')
731 # see http://lopica.sourceforge.net/os.html for a list of possible values
732 if s == 'Mac OS X':
733 return 'darwin'
734 elif s.startswith('Windows '):
735 return 'win32'
736 elif s == 'OS/2':
737 return 'os2'
738 elif s == 'HP-UX':
739 return 'hp-ux'
740 elif s in ('SunOS', 'Solaris'):
741 return 'sunos'
742 else: s = s.lower()
744 # powerpc == darwin for our purposes
745 if s == 'powerpc':
746 return 'darwin'
747 if s == 'win32' or s == 'os2':
748 return s
749 if s == 'cli' and os.name == 'nt':
750 # ironpython is only on windows as far as we know
751 return 'win32'
752 return re.split(r'\d+$', s)[0]
754 def nada(*k, **kw):
756 Does nothing
758 :return: None
760 pass
762 class Timer(object):
764 Simple object for timing the execution of commands.
765 Its string representation is the duration::
767 from waflib.Utils import Timer
768 timer = Timer()
769 a_few_operations()
770 s = str(timer)
772 def __init__(self):
773 self.start_time = self.now()
775 def __str__(self):
776 delta = self.now() - self.start_time
777 if not isinstance(delta, datetime.timedelta):
778 delta = datetime.timedelta(seconds=delta)
779 days = delta.days
780 hours, rem = divmod(delta.seconds, 3600)
781 minutes, seconds = divmod(rem, 60)
782 seconds += delta.microseconds * 1e-6
783 result = ''
784 if days:
785 result += '%dd' % days
786 if days or hours:
787 result += '%dh' % hours
788 if days or hours or minutes:
789 result += '%dm' % minutes
790 return '%s%.3fs' % (result, seconds)
792 def now(self):
793 return datetime.datetime.utcnow()
795 if hasattr(time, 'perf_counter'):
796 def now(self):
797 return time.perf_counter()
799 def read_la_file(path):
801 Reads property files, used by msvc.py
803 :param path: file to read
804 :type path: string
806 sp = re.compile(r'^([^=]+)=\'(.*)\'$')
807 dc = {}
808 for line in readf(path).splitlines():
809 try:
810 _, left, right, _ = sp.split(line.strip())
811 dc[left] = right
812 except ValueError:
813 pass
814 return dc
816 def run_once(fun):
818 Decorator: let a function cache its results, use like this::
820 @run_once
821 def foo(k):
822 return 345*2343
824 .. note:: in practice this can cause memory leaks, prefer a :py:class:`waflib.Utils.lru_cache`
826 :param fun: function to execute
827 :type fun: function
828 :return: the return value of the function executed
830 cache = {}
831 def wrap(*k):
832 try:
833 return cache[k]
834 except KeyError:
835 ret = fun(*k)
836 cache[k] = ret
837 return ret
838 wrap.__cache__ = cache
839 wrap.__name__ = fun.__name__
840 return wrap
842 def get_registry_app_path(key, filename):
844 Returns the value of a registry key for an executable
846 :type key: string
847 :type filename: list of string
849 if not winreg:
850 return None
851 try:
852 result = winreg.QueryValue(key, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s.exe" % filename[0])
853 except OSError:
854 pass
855 else:
856 if os.path.isfile(result):
857 return result
859 def lib64():
861 Guess the default ``/usr/lib`` extension for 64-bit applications
863 :return: '64' or ''
864 :rtype: string
866 # default settings for /usr/lib
867 if os.sep == '/':
868 if platform.architecture()[0] == '64bit':
869 if os.path.exists('/usr/lib64') and not os.path.exists('/usr/lib32'):
870 return '64'
871 return ''
873 def loose_version(ver_str):
874 # private for the time being!
875 # see #2402
876 lst = re.split(r'([.]|\\d+|[a-zA-Z])', ver_str)
877 ver = []
878 for i, val in enumerate(lst):
879 try:
880 ver.append(int(val))
881 except ValueError:
882 if val != '.':
883 ver.append(val)
884 return ver
886 def sane_path(p):
887 # private function for the time being!
888 return os.path.abspath(os.path.expanduser(p))
890 process_pool = []
892 List of processes started to execute sub-process commands
895 def get_process():
897 Returns a process object that can execute commands as sub-processes
899 :rtype: subprocess.Popen
901 try:
902 return process_pool.pop()
903 except IndexError:
904 filepath = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'processor.py'
905 cmd = [sys.executable, '-c', readf(filepath)]
906 return subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0, close_fds=not is_win32)
908 def run_prefork_process(cmd, kwargs, cargs):
910 Delegates process execution to a pre-forked process instance.
912 if not kwargs.get('env'):
913 kwargs['env'] = dict(os.environ)
914 try:
915 obj = base64.b64encode(cPickle.dumps([cmd, kwargs, cargs]))
916 except (TypeError, AttributeError):
917 return run_regular_process(cmd, kwargs, cargs)
919 proc = get_process()
920 if not proc:
921 return run_regular_process(cmd, kwargs, cargs)
923 proc.stdin.write(obj)
924 proc.stdin.write('\n'.encode())
925 proc.stdin.flush()
926 obj = proc.stdout.readline()
927 if not obj:
928 raise OSError('Preforked sub-process %r died' % proc.pid)
930 process_pool.append(proc)
931 lst = cPickle.loads(base64.b64decode(obj))
932 # Jython wrapper failures (bash/execvp)
933 assert len(lst) == 5
934 ret, out, err, ex, trace = lst
935 if ex:
936 if ex == 'OSError':
937 raise OSError(trace)
938 elif ex == 'ValueError':
939 raise ValueError(trace)
940 elif ex == 'TimeoutExpired':
941 exc = TimeoutExpired(cmd, timeout=cargs['timeout'], output=out)
942 exc.stderr = err
943 raise exc
944 else:
945 raise Exception(trace)
946 return ret, out, err
948 def lchown(path, user=-1, group=-1):
950 Change the owner/group of a path, raises an OSError if the
951 ownership change fails.
953 :param user: user to change
954 :type user: int or str
955 :param group: group to change
956 :type group: int or str
958 if isinstance(user, str):
959 import pwd
960 entry = pwd.getpwnam(user)
961 if not entry:
962 raise OSError('Unknown user %r' % user)
963 user = entry[2]
964 if isinstance(group, str):
965 import grp
966 entry = grp.getgrnam(group)
967 if not entry:
968 raise OSError('Unknown group %r' % group)
969 group = entry[2]
970 return os.lchown(path, user, group)
972 def run_regular_process(cmd, kwargs, cargs={}):
974 Executes a subprocess command by using subprocess.Popen
976 proc = subprocess.Popen(cmd, **kwargs)
977 if kwargs.get('stdout') or kwargs.get('stderr'):
978 try:
979 out, err = proc.communicate(**cargs)
980 except TimeoutExpired:
981 if kwargs.get('start_new_session') and hasattr(os, 'killpg'):
982 os.killpg(proc.pid, signal.SIGKILL)
983 else:
984 proc.kill()
985 out, err = proc.communicate()
986 exc = TimeoutExpired(proc.args, timeout=cargs['timeout'], output=out)
987 exc.stderr = err
988 raise exc
989 status = proc.returncode
990 else:
991 out, err = (None, None)
992 try:
993 status = proc.wait(**cargs)
994 except TimeoutExpired as e:
995 if kwargs.get('start_new_session') and hasattr(os, 'killpg'):
996 os.killpg(proc.pid, signal.SIGKILL)
997 else:
998 proc.kill()
999 proc.wait()
1000 raise e
1001 return status, out, err
1003 def run_process(cmd, kwargs, cargs={}):
1005 Executes a subprocess by using a pre-forked process when possible
1006 or falling back to subprocess.Popen. See :py:func:`waflib.Utils.run_prefork_process`
1007 and :py:func:`waflib.Utils.run_regular_process`
1009 if kwargs.get('stdout') and kwargs.get('stderr'):
1010 return run_prefork_process(cmd, kwargs, cargs)
1011 else:
1012 return run_regular_process(cmd, kwargs, cargs)
1014 def alloc_process_pool(n, force=False):
1016 Allocates an amount of processes to the default pool so its size is at least *n*.
1017 It is useful to call this function early so that the pre-forked
1018 processes use as little memory as possible.
1020 :param n: pool size
1021 :type n: integer
1022 :param force: if True then *n* more processes are added to the existing pool
1023 :type force: bool
1025 # mandatory on python2, unnecessary on python >= 3.2
1026 global run_process, get_process, alloc_process_pool
1027 if not force:
1028 n = max(n - len(process_pool), 0)
1029 try:
1030 lst = [get_process() for x in range(n)]
1031 except OSError:
1032 run_process = run_regular_process
1033 get_process = alloc_process_pool = nada
1034 else:
1035 for x in lst:
1036 process_pool.append(x)
1038 def atexit_pool():
1039 for k in process_pool:
1040 try:
1041 os.kill(k.pid, 9)
1042 except OSError:
1043 pass
1044 else:
1045 k.wait()
1046 # see #1889
1047 if (sys.hexversion<0x207000f and not is_win32) or sys.hexversion>=0x306000f:
1048 atexit.register(atexit_pool)
1050 if os.environ.get('WAF_NO_PREFORK') or sys.platform == 'cli' or not sys.executable:
1051 run_process = run_regular_process
1052 get_process = alloc_process_pool = nada