summaryrefslogtreecommitdiff
path: root/buildtools/wafadmin/Utils.py
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2012-01-04 00:31:27 +0100
committerJelmer Vernooij <jelmer@samba.org>2012-01-04 22:34:20 +0100
commit4f4bce5301ffd8c12aed1b108affa1a75feefb67 (patch)
tree1607accd70a2c37a9b996f7b42ec926b014cfe5b /buildtools/wafadmin/Utils.py
parent1b45f2aed86dda9fda6e6bcf1c9c7cbdc471c18d (diff)
downloadsamba-4f4bce5301ffd8c12aed1b108affa1a75feefb67.tar.gz
samba-4f4bce5301ffd8c12aed1b108affa1a75feefb67.tar.bz2
samba-4f4bce5301ffd8c12aed1b108affa1a75feefb67.zip
Include waf as an extracted source directory, rather than as a one-in-a-file script.
Diffstat (limited to 'buildtools/wafadmin/Utils.py')
-rw-r--r--buildtools/wafadmin/Utils.py726
1 files changed, 726 insertions, 0 deletions
diff --git a/buildtools/wafadmin/Utils.py b/buildtools/wafadmin/Utils.py
new file mode 100644
index 0000000000..41dad570eb
--- /dev/null
+++ b/buildtools/wafadmin/Utils.py
@@ -0,0 +1,726 @@
+#!/usr/bin/env python
+# encoding: utf-8
+# Thomas Nagy, 2005 (ita)
+
+"""
+Utilities, the stable ones are the following:
+
+* h_file: compute a unique value for a file (hash), it uses
+ the module fnv if it is installed (see waf/utils/fnv & http://code.google.com/p/waf/wiki/FAQ)
+ else, md5 (see the python docs)
+
+ For large projects (projects with more than 15000 files) or slow hard disks and filesystems (HFS)
+ it is possible to use a hashing based on the path and the size (may give broken cache results)
+ The method h_file MUST raise an OSError if the file is a folder
+
+ import stat
+ def h_file(filename):
+ st = os.stat(filename)
+ if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
+ m = Utils.md5()
+ m.update(str(st.st_mtime))
+ m.update(str(st.st_size))
+ m.update(filename)
+ return m.digest()
+
+ To replace the function in your project, use something like this:
+ import Utils
+ Utils.h_file = h_file
+
+* h_list
+* h_fun
+* get_term_cols
+* ordered_dict
+
+"""
+
+import os, sys, imp, string, errno, traceback, inspect, re, shutil, datetime, gc
+
+# In python 3.0 we can get rid of all this
+try: from UserDict import UserDict
+except ImportError: from collections import UserDict
+if sys.hexversion >= 0x2060000 or os.name == 'java':
+ import subprocess as pproc
+else:
+ import pproc
+import Logs
+from Constants import *
+
+try:
+ from collections import deque
+except ImportError:
+ class deque(list):
+ def popleft(self):
+ return self.pop(0)
+
+is_win32 = sys.platform == 'win32'
+
+try:
+ # defaultdict in python 2.5
+ from collections import defaultdict as DefaultDict
+except ImportError:
+ class DefaultDict(dict):
+ def __init__(self, default_factory):
+ super(DefaultDict, self).__init__()
+ self.default_factory = default_factory
+ def __getitem__(self, key):
+ try:
+ return super(DefaultDict, self).__getitem__(key)
+ except KeyError:
+ value = self.default_factory()
+ self[key] = value
+ return value
+
+class WafError(Exception):
+ def __init__(self, *args):
+ self.args = args
+ try:
+ self.stack = traceback.extract_stack()
+ except:
+ pass
+ Exception.__init__(self, *args)
+ def __str__(self):
+ return str(len(self.args) == 1 and self.args[0] or self.args)
+
+class WscriptError(WafError):
+ def __init__(self, message, wscript_file=None):
+ if wscript_file:
+ self.wscript_file = wscript_file
+ self.wscript_line = None
+ else:
+ try:
+ (self.wscript_file, self.wscript_line) = self.locate_error()
+ except:
+ (self.wscript_file, self.wscript_line) = (None, None)
+
+ msg_file_line = ''
+ if self.wscript_file:
+ msg_file_line = "%s:" % self.wscript_file
+ if self.wscript_line:
+ msg_file_line += "%s:" % self.wscript_line
+ err_message = "%s error: %s" % (msg_file_line, message)
+ WafError.__init__(self, err_message)
+
+ def locate_error(self):
+ stack = traceback.extract_stack()
+ stack.reverse()
+ for frame in stack:
+ file_name = os.path.basename(frame[0])
+ is_wscript = (file_name == WSCRIPT_FILE or file_name == WSCRIPT_BUILD_FILE)
+ if is_wscript:
+ return (frame[0], frame[1])
+ return (None, None)
+
+indicator = is_win32 and '\x1b[A\x1b[K%s%s%s\r' or '\x1b[K%s%s%s\r'
+
+try:
+ from fnv import new as md5
+ import Constants
+ Constants.SIG_NIL = 'signofnv'
+
+ def h_file(filename):
+ m = md5()
+ try:
+ m.hfile(filename)
+ x = m.digest()
+ if x is None: raise OSError("not a file")
+ return x
+ except SystemError:
+ raise OSError("not a file" + filename)
+
+except ImportError:
+ try:
+ try:
+ from hashlib import md5
+ except ImportError:
+ from md5 import md5
+
+ def h_file(filename):
+ f = open(filename, 'rb')
+ m = md5()
+ while (filename):
+ filename = f.read(100000)
+ m.update(filename)
+ f.close()
+ return m.digest()
+ except ImportError:
+ # portability fixes may be added elsewhere (although, md5 should be everywhere by now)
+ md5 = None
+
+class ordered_dict(UserDict):
+ def __init__(self, dict = None):
+ self.allkeys = []
+ UserDict.__init__(self, dict)
+
+ def __delitem__(self, key):
+ self.allkeys.remove(key)
+ UserDict.__delitem__(self, key)
+
+ def __setitem__(self, key, item):
+ if key not in self.allkeys: self.allkeys.append(key)
+ UserDict.__setitem__(self, key, item)
+
+def exec_command(s, **kw):
+ if 'log' in kw:
+ kw['stdout'] = kw['stderr'] = kw['log']
+ del(kw['log'])
+ kw['shell'] = isinstance(s, str)
+
+ try:
+ proc = pproc.Popen(s, **kw)
+ return proc.wait()
+ except OSError:
+ return -1
+
+if is_win32:
+ def exec_command(s, **kw):
+ if 'log' in kw:
+ kw['stdout'] = kw['stderr'] = kw['log']
+ del(kw['log'])
+ kw['shell'] = isinstance(s, str)
+
+ if len(s) > 2000:
+ startupinfo = pproc.STARTUPINFO()
+ startupinfo.dwFlags |= pproc.STARTF_USESHOWWINDOW
+ kw['startupinfo'] = startupinfo
+
+ try:
+ if 'stdout' not in kw:
+ kw['stdout'] = pproc.PIPE
+ kw['stderr'] = pproc.PIPE
+ kw['universal_newlines'] = True
+ proc = pproc.Popen(s,**kw)
+ (stdout, stderr) = proc.communicate()
+ Logs.info(stdout)
+ if stderr:
+ Logs.error(stderr)
+ return proc.returncode
+ else:
+ proc = pproc.Popen(s,**kw)
+ return proc.wait()
+ except OSError:
+ return -1
+
+listdir = os.listdir
+if is_win32:
+ def listdir_win32(s):
+ if re.match('^[A-Za-z]:$', s):
+ # os.path.isdir fails if s contains only the drive name... (x:)
+ s += os.sep
+ if not os.path.isdir(s):
+ e = OSError()
+ e.errno = errno.ENOENT
+ raise e
+ return os.listdir(s)
+ listdir = listdir_win32
+
+def waf_version(mini = 0x010000, maxi = 0x100000):
+ "Halts if the waf version is wrong"
+ ver = HEXVERSION
+ try: min_val = mini + 0
+ except TypeError: min_val = int(mini.replace('.', '0'), 16)
+
+ if min_val > ver:
+ Logs.error("waf version should be at least %s (%s found)" % (mini, ver))
+ sys.exit(1)
+
+ try: max_val = maxi + 0
+ except TypeError: max_val = int(maxi.replace('.', '0'), 16)
+
+ if max_val < ver:
+ Logs.error("waf version should be at most %s (%s found)" % (maxi, ver))
+ sys.exit(1)
+
+def python_24_guard():
+ if sys.hexversion < 0x20400f0 or sys.hexversion >= 0x3000000:
+ raise ImportError("Waf requires Python >= 2.3 but the raw source requires Python 2.4, 2.5 or 2.6")
+
+def ex_stack():
+ exc_type, exc_value, tb = sys.exc_info()
+ if Logs.verbose > 1:
+ exc_lines = traceback.format_exception(exc_type, exc_value, tb)
+ return ''.join(exc_lines)
+ return str(exc_value)
+
+def to_list(sth):
+ if isinstance(sth, str):
+ return sth.split()
+ else:
+ return sth
+
+g_loaded_modules = {}
+"index modules by absolute path"
+
+g_module=None
+"the main module is special"
+
+def load_module(file_path, name=WSCRIPT_FILE):
+ "this function requires an absolute path"
+ try:
+ return g_loaded_modules[file_path]
+ except KeyError:
+ pass
+
+ module = imp.new_module(name)
+
+ try:
+ code = readf(file_path, m='rU')
+ except (IOError, OSError):
+ raise WscriptError('Could not read the file %r' % file_path)
+
+ module.waf_hash_val = code
+
+ dt = os.path.dirname(file_path)
+ sys.path.insert(0, dt)
+ try:
+ exec(compile(code, file_path, 'exec'), module.__dict__)
+ except Exception:
+ exc_type, exc_value, tb = sys.exc_info()
+ raise WscriptError("".join(traceback.format_exception(exc_type, exc_value, tb)), file_path)
+ sys.path.remove(dt)
+
+ g_loaded_modules[file_path] = module
+
+ return module
+
+def set_main_module(file_path):
+ "Load custom options, if defined"
+ global g_module
+ g_module = load_module(file_path, 'wscript_main')
+ g_module.root_path = file_path
+
+ try:
+ g_module.APPNAME
+ except:
+ g_module.APPNAME = 'noname'
+ try:
+ g_module.VERSION
+ except:
+ g_module.VERSION = '1.0'
+
+ # note: to register the module globally, use the following:
+ # sys.modules['wscript_main'] = g_module
+
+def to_hashtable(s):
+ "used for importing env files"
+ tbl = {}
+ lst = s.split('\n')
+ for line in lst:
+ if not line: continue
+ mems = line.split('=')
+ tbl[mems[0]] = mems[1]
+ return tbl
+
+def get_term_cols():
+ "console width"
+ return 80
+try:
+ import struct, fcntl, termios
+except ImportError:
+ pass
+else:
+ if Logs.got_tty:
+ def myfun():
+ dummy_lines, cols = struct.unpack("HHHH", \
+ fcntl.ioctl(sys.stderr.fileno(),termios.TIOCGWINSZ , \
+ struct.pack("HHHH", 0, 0, 0, 0)))[:2]
+ return cols
+ # we actually try the function once to see if it is suitable
+ try:
+ myfun()
+ except:
+ pass
+ else:
+ get_term_cols = myfun
+
+rot_idx = 0
+rot_chr = ['\\', '|', '/', '-']
+"the rotation character in the progress bar"
+
+
+def split_path(path):
+ return path.split('/')
+
+def split_path_cygwin(path):
+ if path.startswith('//'):
+ ret = path.split('/')[2:]
+ ret[0] = '/' + ret[0]
+ return ret
+ return path.split('/')
+
+re_sp = re.compile('[/\\\\]')
+def split_path_win32(path):
+ if path.startswith('\\\\'):
+ ret = re.split(re_sp, path)[2:]
+ ret[0] = '\\' + ret[0]
+ return ret
+ return re.split(re_sp, path)
+
+if sys.platform == 'cygwin':
+ split_path = split_path_cygwin
+elif is_win32:
+ split_path = split_path_win32
+
+def copy_attrs(orig, dest, names, only_if_set=False):
+ for a in to_list(names):
+ u = getattr(orig, a, ())
+ if u or not only_if_set:
+ setattr(dest, a, u)
+
+def def_attrs(cls, **kw):
+ '''
+ set attributes for class.
+ @param cls [any class]: the class to update the given attributes in.
+ @param kw [dictionary]: dictionary of attributes names and values.
+
+ if the given class hasn't one (or more) of these attributes, add the attribute with its value to the class.
+ '''
+ for k, v in kw.iteritems():
+ if not hasattr(cls, k):
+ setattr(cls, k, v)
+
+def quote_define_name(path):
+ fu = re.compile("[^a-zA-Z0-9]").sub("_", path)
+ fu = fu.upper()
+ return fu
+
+def quote_whitespace(path):
+ return (path.strip().find(' ') > 0 and '"%s"' % path or path).replace('""', '"')
+
+def trimquotes(s):
+ if not s: return ''
+ s = s.rstrip()
+ if s[0] == "'" and s[-1] == "'": return s[1:-1]
+ return s
+
+def h_list(lst):
+ m = md5()
+ m.update(str(lst))
+ return m.digest()
+
+def h_fun(fun):
+ try:
+ return fun.code
+ except AttributeError:
+ try:
+ h = inspect.getsource(fun)
+ except IOError:
+ h = "nocode"
+ try:
+ fun.code = h
+ except AttributeError:
+ pass
+ return h
+
+def pprint(col, str, label='', sep='\n'):
+ "print messages in color"
+ sys.stderr.write("%s%s%s %s%s" % (Logs.colors(col), str, Logs.colors.NORMAL, label, sep))
+
+def check_dir(dir):
+ """If a folder doesn't exists, create it."""
+ try:
+ os.stat(dir)
+ except OSError:
+ try:
+ os.makedirs(dir)
+ except OSError, e:
+ raise WafError("Cannot create folder '%s' (original error: %s)" % (dir, e))
+
+def cmd_output(cmd, **kw):
+
+ silent = False
+ if 'silent' in kw:
+ silent = kw['silent']
+ del(kw['silent'])
+
+ if 'e' in kw:
+ tmp = kw['e']
+ del(kw['e'])
+ kw['env'] = tmp
+
+ kw['shell'] = isinstance(cmd, str)
+ kw['stdout'] = pproc.PIPE
+ if silent:
+ kw['stderr'] = pproc.PIPE
+
+ try:
+ p = pproc.Popen(cmd, **kw)
+ output = p.communicate()[0]
+ except OSError, e:
+ raise ValueError(str(e))
+
+ if p.returncode:
+ if not silent:
+ msg = "command execution failed: %s -> %r" % (cmd, str(output))
+ raise ValueError(msg)
+ output = ''
+ return output
+
+reg_subst = re.compile(r"(\\\\)|(\$\$)|\$\{([^}]+)\}")
+def subst_vars(expr, params):
+ "substitute ${PREFIX}/bin in /usr/local/bin"
+ def repl_var(m):
+ if m.group(1):
+ return '\\'
+ if m.group(2):
+ return '$'
+ try:
+ # environments may contain lists
+ return params.get_flat(m.group(3))
+ except AttributeError:
+ return params[m.group(3)]
+ return reg_subst.sub(repl_var, expr)
+
+def unversioned_sys_platform_to_binary_format(unversioned_sys_platform):
+ "infers the binary format from the unversioned_sys_platform name."
+
+ if unversioned_sys_platform in ('linux', 'freebsd', 'netbsd', 'openbsd', 'sunos', 'gnu'):
+ return 'elf'
+ elif unversioned_sys_platform == 'darwin':
+ return 'mac-o'
+ elif unversioned_sys_platform in ('win32', 'cygwin', 'uwin', 'msys'):
+ return 'pe'
+ # TODO we assume all other operating systems are elf, which is not true.
+ # we may set this to 'unknown' and have ccroot and other tools handle the case "gracefully" (whatever that means).
+ return 'elf'
+
+def unversioned_sys_platform():
+ """returns an unversioned name from sys.platform.
+ sys.plaform is not very well defined and depends directly on the python source tree.
+ The version appended to the names is unreliable as it's taken from the build environment at the time python was built,
+ i.e., it's possible to get freebsd7 on a freebsd8 system.
+ So we remove the version from the name, except for special cases where the os has a stupid name like os2 or win32.
+ Some possible values of sys.platform are, amongst others:
+ aix3 aix4 atheos beos5 darwin freebsd2 freebsd3 freebsd4 freebsd5 freebsd6 freebsd7
+ generic gnu0 irix5 irix6 linux2 mac netbsd1 next3 os2emx riscos sunos5 unixware7
+ Investigating the python source tree may reveal more values.
+ """
+ s = sys.platform
+ if s == 'java':
+ # The real OS is hidden under the JVM.
+ from java.lang import System
+ s = System.getProperty('os.name')
+ # see http://lopica.sourceforge.net/os.html for a list of possible values
+ if s == 'Mac OS X':
+ return 'darwin'
+ elif s.startswith('Windows '):
+ return 'win32'
+ elif s == 'OS/2':
+ return 'os2'
+ elif s == 'HP-UX':
+ return 'hpux'
+ elif s in ('SunOS', 'Solaris'):
+ return 'sunos'
+ else: s = s.lower()
+ if s == 'win32' or s.endswith('os2') and s != 'sunos2': return s
+ return re.split('\d+$', s)[0]
+
+#@deprecated('use unversioned_sys_platform instead')
+def detect_platform():
+ """this function has been in the Utils module for some time.
+ It's hard to guess what people have used it for.
+ It seems its goal is to return an unversionned sys.platform, but it's not handling all platforms.
+ For example, the version is not removed on freebsd and netbsd, amongst others.
+ """
+ s = sys.platform
+
+ # known POSIX
+ for x in 'cygwin linux irix sunos hpux aix darwin gnu'.split():
+ # sys.platform may be linux2
+ if s.find(x) >= 0:
+ return x
+
+ # unknown POSIX
+ if os.name in 'posix java os2'.split():
+ return os.name
+
+ return s
+
+def load_tool(tool, tooldir=None):
+ '''
+ load_tool: import a Python module, optionally using several directories.
+ @param tool [string]: name of tool to import.
+ @param tooldir [list]: directories to look for the tool.
+ @return: the loaded module.
+
+ Warning: this function is not thread-safe: plays with sys.path,
+ so must run in sequence.
+ '''
+ if tooldir:
+ assert isinstance(tooldir, list)
+ sys.path = tooldir + sys.path
+ else:
+ tooldir = []
+ try:
+ return __import__(tool)
+ finally:
+ for dt in tooldir:
+ sys.path.remove(dt)
+
+def readf(fname, m='r'):
+ "get the contents of a file, it is not used anywhere for the moment"
+ f = open(fname, m)
+ try:
+ txt = f.read()
+ finally:
+ f.close()
+ return txt
+
+def nada(*k, **kw):
+ """A function that does nothing"""
+ pass
+
+def diff_path(top, subdir):
+ """difference between two absolute paths"""
+ top = os.path.normpath(top).replace('\\', '/').split('/')
+ subdir = os.path.normpath(subdir).replace('\\', '/').split('/')
+ if len(top) == len(subdir): return ''
+ diff = subdir[len(top) - len(subdir):]
+ return os.path.join(*diff)
+
+class Context(object):
+ """A base class for commands to be executed from Waf scripts"""
+
+ def set_curdir(self, dir):
+ self.curdir_ = dir
+
+ def get_curdir(self):
+ try:
+ return self.curdir_
+ except AttributeError:
+ self.curdir_ = os.getcwd()
+ return self.get_curdir()
+
+ curdir = property(get_curdir, set_curdir)
+
+ def recurse(self, dirs, name=''):
+ """The function for calling scripts from folders, it tries to call wscript + function_name
+ and if that file does not exist, it will call the method 'function_name' from a file named wscript
+ the dirs can be a list of folders or a string containing space-separated folder paths
+ """
+ if not name:
+ name = inspect.stack()[1][3]
+
+ if isinstance(dirs, str):
+ dirs = to_list(dirs)
+
+ for x in dirs:
+ if os.path.isabs(x):
+ nexdir = x
+ else:
+ nexdir = os.path.join(self.curdir, x)
+
+ base = os.path.join(nexdir, WSCRIPT_FILE)
+ file_path = base + '_' + name
+
+ try:
+ txt = readf(file_path, m='rU')
+ except (OSError, IOError):
+ try:
+ module = load_module(base)
+ except OSError:
+ raise WscriptError('No such script %s' % base)
+
+ try:
+ f = module.__dict__[name]
+ except KeyError:
+ raise WscriptError('No function %s defined in %s' % (name, base))
+
+ if getattr(self.__class__, 'pre_recurse', None):
+ self.pre_recurse(f, base, nexdir)
+ old = self.curdir
+ self.curdir = nexdir
+ try:
+ f(self)
+ finally:
+ self.curdir = old
+ if getattr(self.__class__, 'post_recurse', None):
+ self.post_recurse(module, base, nexdir)
+ else:
+ dc = {'ctx': self}
+ if getattr(self.__class__, 'pre_recurse', None):
+ dc = self.pre_recurse(txt, file_path, nexdir)
+ old = self.curdir
+ self.curdir = nexdir
+ try:
+ try:
+ exec(compile(txt, file_path, 'exec'), dc)
+ except Exception:
+ exc_type, exc_value, tb = sys.exc_info()
+ raise WscriptError("".join(traceback.format_exception(exc_type, exc_value, tb)), base)
+ finally:
+ self.curdir = old
+ if getattr(self.__class__, 'post_recurse', None):
+ self.post_recurse(txt, file_path, nexdir)
+
+if is_win32:
+ old = shutil.copy2
+ def copy2(src, dst):
+ old(src, dst)
+ shutil.copystat(src, src)
+ setattr(shutil, 'copy2', copy2)
+
+def zip_folder(dir, zip_file_name, prefix):
+ """
+ prefix represents the app to add in the archive
+ """
+ import zipfile
+ zip = zipfile.ZipFile(zip_file_name, 'w', compression=zipfile.ZIP_DEFLATED)
+ base = os.path.abspath(dir)
+
+ if prefix:
+ if prefix[-1] != os.sep:
+ prefix += os.sep
+
+ n = len(base)
+ for root, dirs, files in os.walk(base):
+ for f in files:
+ archive_name = prefix + root[n:] + os.sep + f
+ zip.write(root + os.sep + f, archive_name, zipfile.ZIP_DEFLATED)
+ zip.close()
+
+def get_elapsed_time(start):
+ "Format a time delta (datetime.timedelta) using the format DdHhMmS.MSs"
+ delta = datetime.datetime.now() - start
+ # cast to int necessary for python 3.0
+ days = int(delta.days)
+ hours = int(delta.seconds / 3600)
+ minutes = int((delta.seconds - hours * 3600) / 60)
+ seconds = delta.seconds - hours * 3600 - minutes * 60 \
+ + float(delta.microseconds) / 1000 / 1000
+ result = ''
+ if days:
+ result += '%dd' % days
+ if days or hours:
+ result += '%dh' % hours
+ if days or hours or minutes:
+ result += '%dm' % minutes
+ return '%s%.3fs' % (result, seconds)
+
+if os.name == 'java':
+ # For Jython (they should really fix the inconsistency)
+ try:
+ gc.disable()
+ gc.enable()
+ except NotImplementedError:
+ gc.disable = gc.enable
+
+def run_once(fun):
+ """
+ decorator, make a function cache its results, use like this:
+
+ @run_once
+ def foo(k):
+ return 345*2343
+ """
+ cache = {}
+ def wrap(k):
+ try:
+ return cache[k]
+ except KeyError:
+ ret = fun(k)
+ cache[k] = ret
+ return ret
+ wrap.__cache__ = cache
+ return wrap
+