~ubuntu-branches/ubuntu/wily/samba/wily

« back to all changes in this revision

Viewing changes to buildtools/wafadmin/Utils.py

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2012-05-15 17:00:56 UTC
  • mfrom: (178.1.1 precise-security) (0.39.27 sid)
  • Revision ID: package-import@ubuntu.com-20120515170056-gludtas4257eb61q
Tags: 2:3.6.5-2ubuntu1
* Merge from Debian unstable, remaining changes: 
  + debian/patches/VERSION.patch:
    - set SAMBA_VERSION_SUFFIX to Ubuntu.
  + debian/smb.conf:
    - add "(Samba, Ubuntu)" to server string.
    - comment out the default [homes] share, and add a comment about
      "valid users = %S" to show users how to restrict access to
      \\server\username to only username.
    - Other changes now in Debian packaging.
  + debian/samba-common.config:
    - Do not change priority to high if dhclient3 is installed.
    - Use priority medium instead of high for the workgroup question.
  + debian/control:
    - Don't build against or suggest ctdb.
    - Add dependency on samba-common-bin to samba.
  + Add ufw integration:
    - Created debian/samba.ufw.profile
    - debian/rules, debian/samba.install: install profile.
    - debian/control: have samba suggest ufw.
  + Add apport hook:
    - Created debian/source_samba.py.
    - debian/rules, debian/samba-common-bin.install: install hook.
  + Switch to upstart:
    - Added debian/samba.{nmbd,smbd}.upstart.
    - debian/samba.logrotate, debian/samba-common.dhcp, debian/samba.if-up:
      Make upstart compatible.
* d/samba.install, d/samba-common-bin.install: Restore apport hook and ufw
  profile (LP: #999764).
* Dropped:
  + debian/patches/CVE-2012-1182-*.patch: fixed in upstream release 3.6.4.
  + debian/patches/CVE-2012-2111.patch: fixed in upstream release 3.6.5.
  + debian/patches/fix-debuglevel-name-conflict.patch: fixed upstream -
    debug_level is no longer used as a global variable name.
  + debian/patches/error-trans.fix-276472: fixed upstream.

Show diffs side-by-side

added added

removed removed

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