Wrong assertion/comparison: Compare value not pointer
[gd/samba-autobuild/.git] / buildtools / wafadmin / Utils.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005 (ita)
4
5 """
6 Utilities, the stable ones are the following:
7
8 * h_file: compute a unique value for a file (hash), it uses
9   the module fnv if it is installed (see waf/utils/fnv & http://code.google.com/p/waf/wiki/FAQ)
10   else, md5 (see the python docs)
11
12   For large projects (projects with more than 15000 files) or slow hard disks and filesystems (HFS)
13   it is possible to use a hashing based on the path and the size (may give broken cache results)
14   The method h_file MUST raise an OSError if the file is a folder
15
16         import stat
17         def h_file(filename):
18                 st = os.stat(filename)
19                 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
20                 m = Utils.md5()
21                 m.update(str(st.st_mtime))
22                 m.update(str(st.st_size))
23                 m.update(filename)
24                 return m.digest()
25
26         To replace the function in your project, use something like this:
27         import Utils
28         Utils.h_file = h_file
29
30 * h_list
31 * h_fun
32 * get_term_cols
33 * ordered_dict
34
35 """
36
37 import os, sys, imp, string, errno, traceback, inspect, re, shutil, datetime, gc
38
39 # In python 3.0 we can get rid of all this
40 try: from UserDict import UserDict
41 except ImportError: from collections import UserDict
42 if sys.hexversion >= 0x2060000 or os.name == 'java':
43         import subprocess as pproc
44 else:
45         import pproc
46 import Logs
47 from Constants import *
48
49 try:
50         from collections import deque
51 except ImportError:
52         class deque(list):
53                 def popleft(self):
54                         return self.pop(0)
55
56 is_win32 = sys.platform == 'win32'
57
58 try:
59         # defaultdict in python 2.5
60         from collections import defaultdict as DefaultDict
61 except ImportError:
62         class DefaultDict(dict):
63                 def __init__(self, default_factory):
64                         super(DefaultDict, self).__init__()
65                         self.default_factory = default_factory
66                 def __getitem__(self, key):
67                         try:
68                                 return super(DefaultDict, self).__getitem__(key)
69                         except KeyError:
70                                 value = self.default_factory()
71                                 self[key] = value
72                                 return value
73
74 class WafError(Exception):
75         def __init__(self, *args):
76                 self.args = args
77                 try:
78                         self.stack = traceback.extract_stack()
79                 except:
80                         pass
81                 Exception.__init__(self, *args)
82         def __str__(self):
83                 return str(len(self.args) == 1 and self.args[0] or self.args)
84
85 class WscriptError(WafError):
86         def __init__(self, message, wscript_file=None):
87                 if wscript_file:
88                         self.wscript_file = wscript_file
89                         self.wscript_line = None
90                 else:
91                         try:
92                                 (self.wscript_file, self.wscript_line) = self.locate_error()
93                         except:
94                                 (self.wscript_file, self.wscript_line) = (None, None)
95
96                 msg_file_line = ''
97                 if self.wscript_file:
98                         msg_file_line = "%s:" % self.wscript_file
99                         if self.wscript_line:
100                                 msg_file_line += "%s:" % self.wscript_line
101                 err_message = "%s error: %s" % (msg_file_line, message)
102                 WafError.__init__(self, err_message)
103
104         def locate_error(self):
105                 stack = traceback.extract_stack()
106                 stack.reverse()
107                 for frame in stack:
108                         file_name = os.path.basename(frame[0])
109                         is_wscript = (file_name == WSCRIPT_FILE or file_name == WSCRIPT_BUILD_FILE)
110                         if is_wscript:
111                                 return (frame[0], frame[1])
112                 return (None, None)
113
114 class WscriptCheckSkipped(WscriptError):
115     pass
116
117 indicator = is_win32 and '\x1b[A\x1b[K%s%s%s\r' or '\x1b[K%s%s%s\r'
118
119 try:
120         from fnv import new as md5
121         import Constants
122         Constants.SIG_NIL = 'signofnv'
123
124         def h_file(filename):
125                 m = md5()
126                 try:
127                         m.hfile(filename)
128                         x = m.digest()
129                         if x is None: raise OSError("not a file")
130                         return x
131                 except SystemError:
132                         raise OSError("not a file" + filename)
133
134 except ImportError:
135         try:
136                 try:
137                         from hashlib import md5
138                 except ImportError:
139                         from md5 import md5
140
141                 def h_file(filename):
142                         f = open(filename, 'rb')
143                         m = md5()
144                         while (filename):
145                                 filename = f.read(100000)
146                                 m.update(filename)
147                         f.close()
148                         return m.digest()
149         except ImportError:
150                 # portability fixes may be added elsewhere (although, md5 should be everywhere by now)
151                 md5 = None
152
153 class ordered_dict(UserDict):
154         def __init__(self, dict = None):
155                 self.allkeys = []
156                 UserDict.__init__(self, dict)
157
158         def __delitem__(self, key):
159                 self.allkeys.remove(key)
160                 UserDict.__delitem__(self, key)
161
162         def __setitem__(self, key, item):
163                 if key not in self.allkeys: self.allkeys.append(key)
164                 UserDict.__setitem__(self, key, item)
165
166 def exec_command(s, **kw):
167         if 'log' in kw:
168                 kw['stdout'] = kw['stderr'] = kw['log']
169                 del(kw['log'])
170         kw['shell'] = isinstance(s, str)
171
172         try:
173                 proc = pproc.Popen(s, **kw)
174                 return proc.wait()
175         except OSError:
176                 return -1
177
178 if is_win32:
179         def exec_command(s, **kw):
180                 if 'log' in kw:
181                         kw['stdout'] = kw['stderr'] = kw['log']
182                         del(kw['log'])
183                 kw['shell'] = isinstance(s, str)
184
185                 if len(s) > 2000:
186                         startupinfo = pproc.STARTUPINFO()
187                         startupinfo.dwFlags |= pproc.STARTF_USESHOWWINDOW
188                         kw['startupinfo'] = startupinfo
189
190                 try:
191                         if 'stdout' not in kw:
192                                 kw['stdout'] = pproc.PIPE
193                                 kw['stderr'] = pproc.PIPE
194                                 kw['universal_newlines'] = True
195                                 proc = pproc.Popen(s,**kw)
196                                 (stdout, stderr) = proc.communicate()
197                                 Logs.info(stdout)
198                                 if stderr:
199                                         Logs.error(stderr)
200                                 return proc.returncode
201                         else:
202                                 proc = pproc.Popen(s,**kw)
203                                 return proc.wait()
204                 except OSError:
205                         return -1
206
207 listdir = os.listdir
208 if is_win32:
209         def listdir_win32(s):
210                 if re.match('^[A-Za-z]:$', s):
211                         # os.path.isdir fails if s contains only the drive name... (x:)
212                         s += os.sep
213                 if not os.path.isdir(s):
214                         e = OSError()
215                         e.errno = errno.ENOENT
216                         raise e
217                 return os.listdir(s)
218         listdir = listdir_win32
219
220 def waf_version(mini = 0x010000, maxi = 0x100000):
221         "Halts if the waf version is wrong"
222         ver = HEXVERSION
223         try: min_val = mini + 0
224         except TypeError: min_val = int(mini.replace('.', '0'), 16)
225
226         if min_val > ver:
227                 Logs.error("waf version should be at least %s (%s found)" % (mini, ver))
228                 sys.exit(1)
229
230         try: max_val = maxi + 0
231         except TypeError: max_val = int(maxi.replace('.', '0'), 16)
232
233         if max_val < ver:
234                 Logs.error("waf version should be at most %s (%s found)" % (maxi, ver))
235                 sys.exit(1)
236
237 def python_24_guard():
238         if sys.hexversion < 0x20400f0 or sys.hexversion >= 0x3000000:
239                 raise ImportError("Waf requires Python >= 2.3 but the raw source requires Python 2.4, 2.5 or 2.6")
240
241 def ex_stack():
242         exc_type, exc_value, tb = sys.exc_info()
243         if Logs.verbose > 1:
244                 exc_lines = traceback.format_exception(exc_type, exc_value, tb)
245                 return ''.join(exc_lines)
246         return str(exc_value)
247
248 def to_list(sth):
249         if isinstance(sth, str):
250                 return sth.split()
251         else:
252                 return sth
253
254 g_loaded_modules = {}
255 "index modules by absolute path"
256
257 g_module=None
258 "the main module is special"
259
260 def load_module(file_path, name=WSCRIPT_FILE):
261         "this function requires an absolute path"
262         try:
263                 return g_loaded_modules[file_path]
264         except KeyError:
265                 pass
266
267         module = imp.new_module(name)
268
269         try:
270                 code = readf(file_path, m='rU')
271         except (IOError, OSError):
272                 raise WscriptError('Could not read the file %r' % file_path)
273
274         module.waf_hash_val = code
275
276         dt = os.path.dirname(file_path)
277         sys.path.insert(0, dt)
278         try:
279                 exec(compile(code, file_path, 'exec'), module.__dict__)
280         except Exception:
281                 exc_type, exc_value, tb = sys.exc_info()
282                 raise WscriptError("".join(traceback.format_exception(exc_type, exc_value, tb)), file_path)
283         sys.path.remove(dt)
284
285         g_loaded_modules[file_path] = module
286
287         return module
288
289 def set_main_module(file_path):
290         "Load custom options, if defined"
291         global g_module
292         g_module = load_module(file_path, 'wscript_main')
293         g_module.root_path = file_path
294
295         try:
296                 g_module.APPNAME
297         except:
298                 g_module.APPNAME = 'noname'
299         try:
300                 g_module.VERSION
301         except:
302                 g_module.VERSION = '1.0'
303
304         # note: to register the module globally, use the following:
305         # sys.modules['wscript_main'] = g_module
306
307 def to_hashtable(s):
308         "used for importing env files"
309         tbl = {}
310         lst = s.split('\n')
311         for line in lst:
312                 if not line: continue
313                 mems = line.split('=')
314                 tbl[mems[0]] = mems[1]
315         return tbl
316
317 def get_term_cols():
318         "console width"
319         return 80
320 try:
321         import struct, fcntl, termios
322 except ImportError:
323         pass
324 else:
325         if Logs.got_tty:
326                 def myfun():
327                         dummy_lines, cols = struct.unpack("HHHH", \
328                         fcntl.ioctl(sys.stderr.fileno(),termios.TIOCGWINSZ , \
329                         struct.pack("HHHH", 0, 0, 0, 0)))[:2]
330                         return cols
331                 # we actually try the function once to see if it is suitable
332                 try:
333                         myfun()
334                 except:
335                         pass
336                 else:
337                         get_term_cols = myfun
338
339 rot_idx = 0
340 rot_chr = ['\\', '|', '/', '-']
341 "the rotation character in the progress bar"
342
343
344 def split_path(path):
345         return path.split('/')
346
347 def split_path_cygwin(path):
348         if path.startswith('//'):
349                 ret = path.split('/')[2:]
350                 ret[0] = '/' + ret[0]
351                 return ret
352         return path.split('/')
353
354 re_sp = re.compile('[/\\\\]')
355 def split_path_win32(path):
356         if path.startswith('\\\\'):
357                 ret = re.split(re_sp, path)[2:]
358                 ret[0] = '\\' + ret[0]
359                 return ret
360         return re.split(re_sp, path)
361
362 if sys.platform == 'cygwin':
363         split_path = split_path_cygwin
364 elif is_win32:
365         split_path = split_path_win32
366
367 def copy_attrs(orig, dest, names, only_if_set=False):
368         for a in to_list(names):
369                 u = getattr(orig, a, ())
370                 if u or not only_if_set:
371                         setattr(dest, a, u)
372
373 def def_attrs(cls, **kw):
374         '''
375         set attributes for class.
376         @param cls [any class]: the class to update the given attributes in.
377         @param kw [dictionary]: dictionary of attributes names and values.
378
379         if the given class hasn't one (or more) of these attributes, add the attribute with its value to the class.
380         '''
381         for k, v in kw.iteritems():
382                 if not hasattr(cls, k):
383                         setattr(cls, k, v)
384
385 def quote_define_name(path):
386         fu = re.compile("[^a-zA-Z0-9]").sub("_", path)
387         fu = fu.upper()
388         return fu
389
390 def quote_whitespace(path):
391         return (path.strip().find(' ') > 0 and '"%s"' % path or path).replace('""', '"')
392
393 def trimquotes(s):
394         if not s: return ''
395         s = s.rstrip()
396         if s[0] == "'" and s[-1] == "'": return s[1:-1]
397         return s
398
399 def h_list(lst):
400         m = md5()
401         m.update(str(lst))
402         return m.digest()
403
404 def h_fun(fun):
405         try:
406                 return fun.code
407         except AttributeError:
408                 try:
409                         h = inspect.getsource(fun)
410                 except IOError:
411                         h = "nocode"
412                 try:
413                         fun.code = h
414                 except AttributeError:
415                         pass
416                 return h
417
418 def pprint(col, str, label='', sep='\n'):
419         "print messages in color"
420         sys.stderr.write("%s%s%s %s%s" % (Logs.colors(col), str, Logs.colors.NORMAL, label, sep))
421
422 def check_dir(dir):
423         """If a folder doesn't exists, create it."""
424         try:
425                 os.stat(dir)
426         except OSError:
427                 try:
428                         os.makedirs(dir)
429                 except OSError, e:
430                         raise WafError("Cannot create folder '%s' (original error: %s)" % (dir, e))
431
432 def cmd_output(cmd, **kw):
433
434         silent = False
435         if 'silent' in kw:
436                 silent = kw['silent']
437                 del(kw['silent'])
438
439         if 'e' in kw:
440                 tmp = kw['e']
441                 del(kw['e'])
442                 kw['env'] = tmp
443
444         kw['shell'] = isinstance(cmd, str)
445         kw['stdout'] = pproc.PIPE
446         if silent:
447                 kw['stderr'] = pproc.PIPE
448
449         try:
450                 p = pproc.Popen(cmd, **kw)
451                 output = p.communicate()[0]
452         except OSError, e:
453                 raise ValueError(str(e))
454
455         if p.returncode:
456                 if not silent:
457                         msg = "command execution failed: %s -> %r" % (cmd, str(output))
458                         raise ValueError(msg)
459                 output = ''
460         return output
461
462 reg_subst = re.compile(r"(\\\\)|(\$\$)|\$\{([^}]+)\}")
463 def subst_vars(expr, params):
464         "substitute ${PREFIX}/bin in /usr/local/bin"
465         def repl_var(m):
466                 if m.group(1):
467                         return '\\'
468                 if m.group(2):
469                         return '$'
470                 try:
471                         # environments may contain lists
472                         return params.get_flat(m.group(3))
473                 except AttributeError:
474                         return params[m.group(3)]
475         return reg_subst.sub(repl_var, expr)
476
477 def unversioned_sys_platform_to_binary_format(unversioned_sys_platform):
478         "infers the binary format from the unversioned_sys_platform name."
479
480         if unversioned_sys_platform in ('linux', 'freebsd', 'netbsd', 'openbsd', 'sunos', 'gnu'):
481                 return 'elf'
482         elif unversioned_sys_platform == 'darwin':
483                 return 'mac-o'
484         elif unversioned_sys_platform in ('win32', 'cygwin', 'uwin', 'msys'):
485                 return 'pe'
486         # TODO we assume all other operating systems are elf, which is not true.
487         # we may set this to 'unknown' and have ccroot and other tools handle the case "gracefully" (whatever that means).
488         return 'elf'
489
490 def unversioned_sys_platform():
491         """returns an unversioned name from sys.platform.
492         sys.plaform is not very well defined and depends directly on the python source tree.
493         The version appended to the names is unreliable as it's taken from the build environment at the time python was built,
494         i.e., it's possible to get freebsd7 on a freebsd8 system.
495         So we remove the version from the name, except for special cases where the os has a stupid name like os2 or win32.
496         Some possible values of sys.platform are, amongst others:
497                 aix3 aix4 atheos beos5 darwin freebsd2 freebsd3 freebsd4 freebsd5 freebsd6 freebsd7
498                 generic gnu0 irix5 irix6 linux2 mac netbsd1 next3 os2emx riscos sunos5 unixware7
499         Investigating the python source tree may reveal more values.
500         """
501         s = sys.platform
502         if s == 'java':
503                 # The real OS is hidden under the JVM.
504                 from java.lang import System
505                 s = System.getProperty('os.name')
506                 # see http://lopica.sourceforge.net/os.html for a list of possible values
507                 if s == 'Mac OS X':
508                         return 'darwin'
509                 elif s.startswith('Windows '):
510                         return 'win32'
511                 elif s == 'OS/2':
512                         return 'os2'
513                 elif s == 'HP-UX':
514                         return 'hpux'
515                 elif s in ('SunOS', 'Solaris'):
516                         return 'sunos'
517                 else: s = s.lower()
518         if s == 'win32' or s.endswith('os2') and s != 'sunos2': return s
519         return re.split('\d+$', s)[0]
520
521 #@deprecated('use unversioned_sys_platform instead')
522 def detect_platform():
523         """this function has been in the Utils module for some time.
524         It's hard to guess what people have used it for.
525         It seems its goal is to return an unversionned sys.platform, but it's not handling all platforms.
526         For example, the version is not removed on freebsd and netbsd, amongst others.
527         """
528         s = sys.platform
529
530         # known POSIX
531         for x in 'cygwin linux irix sunos hpux aix darwin gnu'.split():
532                 # sys.platform may be linux2
533                 if s.find(x) >= 0:
534                         return x
535
536         # unknown POSIX
537         if os.name in 'posix java os2'.split():
538                 return os.name
539
540         return s
541
542 def load_tool(tool, tooldir=None):
543         '''
544         load_tool: import a Python module, optionally using several directories.
545         @param tool [string]: name of tool to import.
546         @param tooldir [list]: directories to look for the tool.
547         @return: the loaded module.
548
549         Warning: this function is not thread-safe: plays with sys.path,
550                                          so must run in sequence.
551         '''
552         if tooldir:
553                 assert isinstance(tooldir, list)
554                 sys.path = tooldir + sys.path
555         else:
556                 tooldir = []
557         try:
558                 return __import__(tool)
559         finally:
560                 for dt in tooldir:
561                         sys.path.remove(dt)
562
563 def readf(fname, m='r'):
564         "get the contents of a file, it is not used anywhere for the moment"
565         f = open(fname, m)
566         try:
567                 txt = f.read()
568         finally:
569                 f.close()
570         return txt
571
572 def nada(*k, **kw):
573         """A function that does nothing"""
574         pass
575
576 def diff_path(top, subdir):
577         """difference between two absolute paths"""
578         top = os.path.normpath(top).replace('\\', '/').split('/')
579         subdir = os.path.normpath(subdir).replace('\\', '/').split('/')
580         if len(top) == len(subdir): return ''
581         diff = subdir[len(top) - len(subdir):]
582         return os.path.join(*diff)
583
584 class Context(object):
585         """A base class for commands to be executed from Waf scripts"""
586
587         def set_curdir(self, dir):
588                 self.curdir_ = dir
589
590         def get_curdir(self):
591                 try:
592                         return self.curdir_
593                 except AttributeError:
594                         self.curdir_ = os.getcwd()
595                         return self.get_curdir()
596
597         curdir = property(get_curdir, set_curdir)
598
599         def recurse(self, dirs, name=''):
600                 """The function for calling scripts from folders, it tries to call wscript + function_name
601                 and if that file does not exist, it will call the method 'function_name' from a file named wscript
602                 the dirs can be a list of folders or a string containing space-separated folder paths
603                 """
604                 if not name:
605                         name = inspect.stack()[1][3]
606
607                 if isinstance(dirs, str):
608                         dirs = to_list(dirs)
609
610                 for x in dirs:
611                         if os.path.isabs(x):
612                                 nexdir = x
613                         else:
614                                 nexdir = os.path.join(self.curdir, x)
615
616                         base = os.path.join(nexdir, WSCRIPT_FILE)
617                         file_path = base + '_' + name
618
619                         try:
620                                 txt = readf(file_path, m='rU')
621                         except (OSError, IOError):
622                                 try:
623                                         module = load_module(base)
624                                 except OSError:
625                                         raise WscriptError('No such script %s' % base)
626
627                                 try:
628                                         f = module.__dict__[name]
629                                 except KeyError:
630                                         raise WscriptError('No function %s defined in %s' % (name, base))
631
632                                 if getattr(self.__class__, 'pre_recurse', None):
633                                         self.pre_recurse(f, base, nexdir)
634                                 old = self.curdir
635                                 self.curdir = nexdir
636                                 try:
637                                         f(self)
638                                 finally:
639                                         self.curdir = old
640                                 if getattr(self.__class__, 'post_recurse', None):
641                                         self.post_recurse(module, base, nexdir)
642                         else:
643                                 dc = {'ctx': self}
644                                 if getattr(self.__class__, 'pre_recurse', None):
645                                         dc = self.pre_recurse(txt, file_path, nexdir)
646                                 old = self.curdir
647                                 self.curdir = nexdir
648                                 try:
649                                         try:
650                                                 exec(compile(txt, file_path, 'exec'), dc)
651                                         except WscriptCheckSkipped:
652                                                 pass
653                                         except Exception:
654                                                 exc_type, exc_value, tb = sys.exc_info()
655                                                 raise WscriptError("".join(traceback.format_exception(exc_type, exc_value, tb)), base)
656                                 finally:
657                                         self.curdir = old
658                                 if getattr(self.__class__, 'post_recurse', None):
659                                         self.post_recurse(txt, file_path, nexdir)
660
661 if is_win32:
662         old = shutil.copy2
663         def copy2(src, dst):
664                 old(src, dst)
665                 shutil.copystat(src, src)
666         setattr(shutil, 'copy2', copy2)
667
668 def zip_folder(dir, zip_file_name, prefix):
669         """
670         prefix represents the app to add in the archive
671         """
672         import zipfile
673         zip = zipfile.ZipFile(zip_file_name, 'w', compression=zipfile.ZIP_DEFLATED)
674         base = os.path.abspath(dir)
675
676         if prefix:
677                 if prefix[-1] != os.sep:
678                         prefix += os.sep
679
680         n = len(base)
681         for root, dirs, files in os.walk(base):
682                 for f in files:
683                         archive_name = prefix + root[n:] + os.sep + f
684                         zip.write(root + os.sep + f, archive_name, zipfile.ZIP_DEFLATED)
685         zip.close()
686
687 def get_elapsed_time(start):
688         "Format a time delta (datetime.timedelta) using the format DdHhMmS.MSs"
689         delta = datetime.datetime.now() - start
690         # cast to int necessary for python 3.0
691         days = int(delta.days)
692         hours = int(delta.seconds / 3600)
693         minutes = int((delta.seconds - hours * 3600) / 60)
694         seconds = delta.seconds - hours * 3600 - minutes * 60 \
695                 + float(delta.microseconds) / 1000 / 1000
696         result = ''
697         if days:
698                 result += '%dd' % days
699         if days or hours:
700                 result += '%dh' % hours
701         if days or hours or minutes:
702                 result += '%dm' % minutes
703         return '%s%.3fs' % (result, seconds)
704
705 if os.name == 'java':
706         # For Jython (they should really fix the inconsistency)
707         try:
708                 gc.disable()
709                 gc.enable()
710         except NotImplementedError:
711                 gc.disable = gc.enable
712
713 def run_once(fun):
714         """
715         decorator, make a function cache its results, use like this:
716
717         @run_once
718         def foo(k):
719                 return 345*2343
720         """
721         cache = {}
722         def wrap(k):
723                 try:
724                         return cache[k]
725                 except KeyError:
726                         ret = fun(k)
727                         cache[k] = ret
728                         return ret
729         wrap.__cache__ = cache
730         return wrap
731