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