~vcs-imports/kupfer/master-new

« back to all changes in this revision

Viewing changes to waflib/Context.py

  • Committer: Ulrik Sverdrup
  • Date: 2012-02-26 17:32:06 UTC
  • mto: This revision was merged to the branch mainline in revision 2918.
  • Revision ID: git-v1:684a1e79e28fa3d9e346bdf2c1659e7d2c6e7718
Add waf-light and waflib from waf-1.6.11

http://code.google.com/p/waf/
Acquired from: http://waf.googlecode.com/files/waf-1.6.11.tar.bz2

"""
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
"""

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# encoding: utf-8
 
3
# Thomas Nagy, 2010 (ita)
 
4
 
 
5
"""
 
6
Classes and functions required for waf commands
 
7
"""
 
8
 
 
9
import os, imp, sys
 
10
from waflib import Utils, Errors, Logs
 
11
import waflib.Node
 
12
 
 
13
# the following 3 constants are updated on each new release (do not touch)
 
14
HEXVERSION=0x1060b00
 
15
"""Constant updated on new releases"""
 
16
 
 
17
WAFVERSION="1.6.11"
 
18
"""Constant updated on new releases"""
 
19
 
 
20
WAFREVISION="a7e69d6b81b04729804754c4d5214da063779a65"
 
21
"""Constant updated on new releases"""
 
22
 
 
23
ABI = 98
 
24
"""Version of the build data cache file format (used in :py:const:`waflib.Context.DBFILE`)"""
 
25
 
 
26
DBFILE = '.wafpickle-%d' % ABI
 
27
"""Name of the pickle file for storing the build data"""
 
28
 
 
29
APPNAME = 'APPNAME'
 
30
"""Default application name (used by ``waf dist``)"""
 
31
 
 
32
VERSION = 'VERSION'
 
33
"""Default application version (used by ``waf dist``)"""
 
34
 
 
35
TOP  = 'top'
 
36
"""The variable name for the top-level directory in wscript files"""
 
37
 
 
38
OUT  = 'out'
 
39
"""The variable name for the output directory in wscript files"""
 
40
 
 
41
WSCRIPT_FILE = 'wscript'
 
42
"""Name of the waf script files"""
 
43
 
 
44
 
 
45
launch_dir = ''
 
46
"""Directory from which waf has been called"""
 
47
run_dir = ''
 
48
"""Location of the wscript file to use as the entry point"""
 
49
top_dir = ''
 
50
"""Location of the project directory (top), if the project was configured"""
 
51
out_dir = ''
 
52
"""Location of the build directory (out), if the project was configured"""
 
53
waf_dir = ''
 
54
"""Directory containing the waf modules"""
 
55
 
 
56
local_repo = ''
 
57
"""Local repository containing additional Waf tools (plugins)"""
 
58
remote_repo = 'http://waf.googlecode.com/git/'
 
59
"""
 
60
Remote directory containing downloadable waf tools. The missing tools can be downloaded by using::
 
61
 
 
62
        $ waf configure --download
 
63
"""
 
64
 
 
65
remote_locs = ['waflib/extras', 'waflib/Tools']
 
66
"""
 
67
Remote directories for use with :py:const:`waflib.Context.remote_repo`
 
68
"""
 
69
 
 
70
g_module = None
 
71
"""
 
72
Module representing the main wscript file (see :py:const:`waflib.Context.run_dir`)
 
73
"""
 
74
 
 
75
STDOUT = 1
 
76
STDERR = -1
 
77
BOTH   = 0
 
78
 
 
79
classes = []
 
80
"""
 
81
List of :py:class:`waflib.Context.Context` subclasses that can be used as waf commands. The classes
 
82
are added automatically by a metaclass.
 
83
"""
 
84
 
 
85
 
 
86
def create_context(cmd_name, *k, **kw):
 
87
        """
 
88
        Create a new :py:class:`waflib.Context.Context` instance corresponding to the given command.
 
89
        Used in particular by :py:func:`waflib.Scripting.run_command`
 
90
 
 
91
        :param cmd_name: command
 
92
        :type cmd_name: string
 
93
        :param k: arguments to give to the context class initializer
 
94
        :type k: list
 
95
        :param k: keyword arguments to give to the context class initializer
 
96
        :type k: dict
 
97
        """
 
98
        global classes
 
99
        for x in classes:
 
100
                if x.cmd == cmd_name:
 
101
                        return x(*k, **kw)
 
102
        ctx = Context(*k, **kw)
 
103
        ctx.fun = cmd_name
 
104
        return ctx
 
105
 
 
106
class store_context(type):
 
107
        """
 
108
        Metaclass for storing the command classes into the list :py:const:`waflib.Context.classes`
 
109
        Context classes must provide an attribute 'cmd' representing the command to execute
 
110
        """
 
111
        def __init__(cls, name, bases, dict):
 
112
                super(store_context, cls).__init__(name, bases, dict)
 
113
                name = cls.__name__
 
114
 
 
115
                if name == 'ctx' or name == 'Context':
 
116
                        return
 
117
 
 
118
                try:
 
119
                        cls.cmd
 
120
                except AttributeError:
 
121
                        raise Errors.WafError('Missing command for the context class %r (cmd)' % name)
 
122
 
 
123
                if not getattr(cls, 'fun', None):
 
124
                        cls.fun = cls.cmd
 
125
 
 
126
                global classes
 
127
                classes.insert(0, cls)
 
128
 
 
129
ctx = store_context('ctx', (object,), {})
 
130
"""Base class for the :py:class:`waflib.Context.Context` classes"""
 
131
 
 
132
class Context(ctx):
 
133
        """
 
134
        Default context for waf commands, and base class for new command contexts.
 
135
 
 
136
        Context objects are passed to top-level functions::
 
137
 
 
138
                def foo(ctx):
 
139
                        print(ctx.__class__.__name__) # waflib.Context.Context
 
140
 
 
141
        Subclasses must define the attribute 'cmd':
 
142
 
 
143
        :param cmd: command to execute as in ``waf cmd``
 
144
        :type cmd: string
 
145
        :param fun: function name to execute when the command is called
 
146
        :type fun: string
 
147
 
 
148
        .. inheritance-diagram:: waflib.Context.Context waflib.Build.BuildContext waflib.Build.InstallContext waflib.Build.UninstallContext waflib.Build.StepContext waflib.Build.ListContext waflib.Configure.ConfigurationContext waflib.Scripting.Dist waflib.Scripting.DistCheck waflib.Build.CleanContext
 
149
 
 
150
        """
 
151
 
 
152
        errors = Errors
 
153
        """
 
154
        Shortcut to :py:mod:`waflib.Errors` provided for convenience
 
155
        """
 
156
 
 
157
        tools = {}
 
158
        """
 
159
        A cache for modules (wscript files) read by :py:meth:`Context.Context.load`
 
160
        """
 
161
 
 
162
        def __init__(self, **kw):
 
163
                try:
 
164
                        rd = kw['run_dir']
 
165
                except KeyError:
 
166
                        global run_dir
 
167
                        rd = run_dir
 
168
 
 
169
                # binds the context to the nodes in use to avoid a context singleton
 
170
                class node_class(waflib.Node.Node):
 
171
                        pass
 
172
                self.node_class = node_class
 
173
                self.node_class.__module__ = "waflib.Node"
 
174
                self.node_class.__name__ = "Nod3"
 
175
                self.node_class.ctx = self
 
176
 
 
177
                self.root = self.node_class('', None)
 
178
                self.cur_script = None
 
179
                self.path = self.root.find_dir(rd)
 
180
 
 
181
                self.stack_path = []
 
182
                self.exec_dict = {'ctx':self, 'conf':self, 'bld':self, 'opt':self}
 
183
                self.logger = None
 
184
 
 
185
        def __hash__(self):
 
186
                """
 
187
                Return a hash value for storing context objects in dicts or sets. The value is not persistent.
 
188
 
 
189
                :return: hash value
 
190
                :rtype: int
 
191
                """
 
192
                return id(self)
 
193
 
 
194
        def load(self, tool_list, *k, **kw):
 
195
                """
 
196
                Load a Waf tool as a module, and try calling the function named :py:const:`waflib.Context.Context.fun` from it.
 
197
                A ``tooldir`` value may be provided as a list of module paths.
 
198
 
 
199
                :type tool_list: list of string or space-separated string
 
200
                :param tool_list: list of Waf tools to use
 
201
                """
 
202
                tools = Utils.to_list(tool_list)
 
203
                path = Utils.to_list(kw.get('tooldir', ''))
 
204
 
 
205
                for t in tools:
 
206
                        module = load_tool(t, path)
 
207
                        fun = getattr(module, kw.get('name', self.fun), None)
 
208
                        if fun:
 
209
                                fun(self)
 
210
 
 
211
        def execute(self):
 
212
                """
 
213
                Execute the command. Redefine this method in subclasses.
 
214
                """
 
215
                global g_module
 
216
                self.recurse([os.path.dirname(g_module.root_path)])
 
217
 
 
218
        def pre_recurse(self, node):
 
219
                """
 
220
                Method executed immediately before a folder is read by :py:meth:`waflib.Context.Context.recurse`. The node given is set
 
221
                as an attribute ``self.cur_script``, and as the current path ``self.path``
 
222
 
 
223
                :param node: script
 
224
                :type node: :py:class:`waflib.Node.Node`
 
225
                """
 
226
                self.stack_path.append(self.cur_script)
 
227
 
 
228
                self.cur_script = node
 
229
                self.path = node.parent
 
230
 
 
231
        def post_recurse(self, node):
 
232
                """
 
233
                Restore ``self.cur_script`` and ``self.path`` right after :py:meth:`waflib.Context.Context.recurse` terminates.
 
234
 
 
235
                :param node: script
 
236
                :type node: :py:class:`waflib.Node.Node`
 
237
                """
 
238
                self.cur_script = self.stack_path.pop()
 
239
                if self.cur_script:
 
240
                        self.path = self.cur_script.parent
 
241
 
 
242
        def recurse(self, dirs, name=None, mandatory=True, once=True):
 
243
                """
 
244
                Run user code from the supplied list of directories.
 
245
                The directories can be either absolute, or relative to the directory
 
246
                of the wscript file. The methods :py:meth:`waflib.Context.Context.pre_recurse` and :py:meth:`waflib.Context.Context.post_recurse`
 
247
                are called immediately before and after a script has been executed.
 
248
 
 
249
                :param dirs: List of directories to visit
 
250
                :type dirs: list of string or space-separated string
 
251
                :param name: Name of function to invoke from the wscript
 
252
                :type  name: string
 
253
                :param mandatory: whether sub wscript files are required to exist
 
254
                :type  mandatory: bool
 
255
                :param once: read the script file once for a particular context
 
256
                :type once: bool
 
257
                """
 
258
                try:
 
259
                        cache = self.recurse_cache
 
260
                except:
 
261
                        cache = self.recurse_cache = {}
 
262
 
 
263
                for d in Utils.to_list(dirs):
 
264
 
 
265
                        if not os.path.isabs(d):
 
266
                                # absolute paths only
 
267
                                d = os.path.join(self.path.abspath(), d)
 
268
 
 
269
                        WSCRIPT     = os.path.join(d, WSCRIPT_FILE)
 
270
                        WSCRIPT_FUN = WSCRIPT + '_' + (name or self.fun)
 
271
 
 
272
                        node = self.root.find_node(WSCRIPT_FUN)
 
273
                        if node and (not once or node not in cache):
 
274
                                cache[node] = True
 
275
                                self.pre_recurse(node)
 
276
                                try:
 
277
                                        function_code = node.read('rU')
 
278
                                        exec(compile(function_code, node.abspath(), 'exec'), self.exec_dict)
 
279
                                finally:
 
280
                                        self.post_recurse(node)
 
281
                        elif not node:
 
282
                                node = self.root.find_node(WSCRIPT)
 
283
                                tup = (node, name or self.fun)
 
284
                                if node and (not once or tup not in cache):
 
285
                                        cache[tup] = True
 
286
                                        self.pre_recurse(node)
 
287
                                        try:
 
288
                                                wscript_module = load_module(node.abspath())
 
289
                                                user_function = getattr(wscript_module, (name or self.fun), None)
 
290
                                                if not user_function:
 
291
                                                        if not mandatory:
 
292
                                                                continue
 
293
                                                        raise Errors.WafError('No function %s defined in %s' % (name or self.fun, node.abspath()))
 
294
                                                user_function(self)
 
295
                                        finally:
 
296
                                                self.post_recurse(node)
 
297
                                elif not node:
 
298
                                        if not mandatory:
 
299
                                                continue
 
300
                                        raise Errors.WafError('No wscript file in directory %s' % d)
 
301
 
 
302
        def exec_command(self, cmd, **kw):
 
303
                """
 
304
                Execute a command and return the exit status. If the context has the attribute 'log',
 
305
                capture and log the process stderr/stdout for logging purposes::
 
306
 
 
307
                        def run(tsk):
 
308
                                ret = tsk.generator.bld.exec_command('touch foo.txt')
 
309
                                return ret
 
310
 
 
311
                Do not confuse this method with :py:meth:`waflib.Context.Context.cmd_and_log` which is used to
 
312
                return the standard output/error values.
 
313
 
 
314
                :param cmd: command argument for subprocess.Popen
 
315
                :param kw: keyword arguments for subprocess.Popen
 
316
                """
 
317
                subprocess = Utils.subprocess
 
318
                kw['shell'] = isinstance(cmd, str)
 
319
                Logs.debug('runner: %r' % cmd)
 
320
                Logs.debug('runner_env: kw=%s' % kw)
 
321
 
 
322
                try:
 
323
                        if self.logger:
 
324
                                # warning: may deadlock with a lot of output (subprocess limitation)
 
325
 
 
326
                                self.logger.info(cmd)
 
327
 
 
328
                                kw['stdout'] = kw['stderr'] = subprocess.PIPE
 
329
                                p = subprocess.Popen(cmd, **kw)
 
330
                                (out, err) = p.communicate()
 
331
                                if out:
 
332
                                        self.logger.debug('out: %s' % out.decode(sys.stdout.encoding or 'iso8859-1'))
 
333
                                if err:
 
334
                                        self.logger.error('err: %s' % err.decode(sys.stdout.encoding or 'iso8859-1'))
 
335
                                return p.returncode
 
336
                        else:
 
337
                                p = subprocess.Popen(cmd, **kw)
 
338
                                return p.wait()
 
339
                except OSError:
 
340
                        return -1
 
341
 
 
342
        def cmd_and_log(self, cmd, **kw):
 
343
                """
 
344
                Execute a command and return stdout if the execution is successful.
 
345
                An exception is thrown when the exit status is non-0. In that case, both stderr and stdout
 
346
                will be bound to the WafError object::
 
347
 
 
348
                        def configure(conf):
 
349
                                out = conf.cmd_and_log(['echo', 'hello'], output=waflib.Context.STDOUT, quiet=waflib.Context.BOTH)
 
350
                                (out, err) = conf.cmd_and_log(['echo', 'hello'], output=waflib.Context.BOTH)
 
351
                                try:
 
352
                                        conf.cmd_and_log(['which', 'someapp'], output=waflib.Context.BOTH)
 
353
                                except Exception as e:
 
354
                                        print(e.stdout, e.stderr)
 
355
 
 
356
                :param cmd: args for subprocess.Popen
 
357
                :param kw: keyword arguments for subprocess.Popen
 
358
                """
 
359
                subprocess = Utils.subprocess
 
360
                kw['shell'] = isinstance(cmd, str)
 
361
                Logs.debug('runner: %r' % cmd)
 
362
 
 
363
                if 'quiet' in kw:
 
364
                        quiet = kw['quiet']
 
365
                        del kw['quiet']
 
366
                else:
 
367
                        quiet = None
 
368
 
 
369
                if 'output' in kw:
 
370
                        to_ret = kw['output']
 
371
                        del kw['output']
 
372
                else:
 
373
                        to_ret = STDOUT
 
374
 
 
375
                kw['stdout'] = kw['stderr'] = subprocess.PIPE
 
376
                if quiet is None:
 
377
                        self.to_log(cmd)
 
378
                try:
 
379
                        p = subprocess.Popen(cmd, **kw)
 
380
                        (out, err) = p.communicate()
 
381
                except Exception as e:
 
382
                        raise Errors.WafError('Execution failure: %s' % str(e), ex=e)
 
383
 
 
384
                if not isinstance(out, str):
 
385
                        out = out.decode(sys.stdout.encoding or 'iso8859-1')
 
386
                if not isinstance(err, str):
 
387
                        err = err.decode(sys.stdout.encoding or 'iso8859-1')
 
388
 
 
389
                if out and quiet != STDOUT and quiet != BOTH:
 
390
                        self.to_log('out: %s' % out)
 
391
                if err and quiet != STDERR and quiet != BOTH:
 
392
                        self.to_log('err: %s' % err)
 
393
 
 
394
                if p.returncode:
 
395
                        e = Errors.WafError('Command %r returned %r' % (cmd, p.returncode))
 
396
                        e.returncode = p.returncode
 
397
                        e.stderr = err
 
398
                        e.stdout = out
 
399
                        raise e
 
400
 
 
401
                if to_ret == BOTH:
 
402
                        return (out, err)
 
403
                elif to_ret == STDERR:
 
404
                        return err
 
405
                return out
 
406
 
 
407
        def fatal(self, msg, ex=None):
 
408
                """
 
409
                Raise a configuration error to interrupt the execution immediately::
 
410
 
 
411
                        def configure(conf):
 
412
                                conf.fatal('a requirement is missing')
 
413
 
 
414
                :param msg: message to display
 
415
                :type msg: string
 
416
                :param ex: optional exception object
 
417
                :type ex: exception
 
418
                """
 
419
                if self.logger:
 
420
                        self.logger.info('from %s: %s' % (self.path.abspath(), msg))
 
421
                try:
 
422
                        msg = '%s\n(complete log in %s)' % (msg, self.logger.handlers[0].baseFilename)
 
423
                except:
 
424
                        pass
 
425
                raise self.errors.ConfigurationError(msg, ex=ex)
 
426
 
 
427
        def to_log(self, msg):
 
428
                """
 
429
                Log some information to the logger (if present), or to stderr. If the message is empty,
 
430
                it is not printed::
 
431
 
 
432
                        def build(bld):
 
433
                                bld.to_log('starting the build')
 
434
 
 
435
                When in doubt, override this method, or provide a logger on the context class.
 
436
 
 
437
                :param msg: message
 
438
                :type msg: string
 
439
                """
 
440
                if not msg:
 
441
                        return
 
442
                if self.logger:
 
443
                        self.logger.info(msg)
 
444
                else:
 
445
                        sys.stderr.write(str(msg))
 
446
                        sys.stderr.flush()
 
447
 
 
448
 
 
449
        def msg(self, msg, result, color=None):
 
450
                """
 
451
                Print a configuration message of the form ``msg: result``.
 
452
                The second part of the message will be in colors. The output
 
453
                can be disabled easly by setting ``in_msg`` to a positive value::
 
454
 
 
455
                        def configure(conf):
 
456
                                self.in_msg = 1
 
457
                                conf.msg('Checking for library foo', 'ok')
 
458
                                # no output
 
459
 
 
460
                :param msg: message to display to the user
 
461
                :type msg: string
 
462
                :param result: result to display
 
463
                :type result: string or boolean
 
464
                :param color: color to use, see :py:const:`waflib.Logs.colors_lst`
 
465
                :type color: string
 
466
                """
 
467
                self.start_msg(msg)
 
468
 
 
469
                if not isinstance(color, str):
 
470
                        color = result and 'GREEN' or 'YELLOW'
 
471
 
 
472
                self.end_msg(result, color)
 
473
 
 
474
        def start_msg(self, msg):
 
475
                """
 
476
                Print the beginning of a 'Checking for xxx' message. See :py:meth:`waflib.Context.Context.msg`
 
477
                """
 
478
                try:
 
479
                        if self.in_msg:
 
480
                                self.in_msg += 1
 
481
                                return
 
482
                except:
 
483
                        self.in_msg = 0
 
484
                self.in_msg += 1
 
485
 
 
486
                try:
 
487
                        self.line_just = max(self.line_just, len(msg))
 
488
                except AttributeError:
 
489
                        self.line_just = max(40, len(msg))
 
490
                for x in (self.line_just * '-', msg):
 
491
                        self.to_log(x)
 
492
                Logs.pprint('NORMAL', "%s :" % msg.ljust(self.line_just), sep='')
 
493
 
 
494
        def end_msg(self, result, color=None):
 
495
                """Print the end of a 'Checking for' message. See :py:meth:`waflib.Context.Context.msg`"""
 
496
                self.in_msg -= 1
 
497
                if self.in_msg:
 
498
                        return
 
499
 
 
500
                defcolor = 'GREEN'
 
501
                if result == True:
 
502
                        msg = 'ok'
 
503
                elif result == False:
 
504
                        msg = 'not found'
 
505
                        defcolor = 'YELLOW'
 
506
                else:
 
507
                        msg = str(result)
 
508
 
 
509
                self.to_log(msg)
 
510
                Logs.pprint(color or defcolor, msg)
 
511
 
 
512
 
 
513
        def load_special_tools(self, var, ban=[]):
 
514
                global waf_dir
 
515
                lst = self.root.find_node(waf_dir).find_node('waflib/extras').ant_glob(var)
 
516
                for x in lst:
 
517
                        if not x.name in ban:
 
518
                                load_tool(x.name.replace('.py', ''))
 
519
 
 
520
cache_modules = {}
 
521
"""
 
522
Dictionary holding already loaded modules, keyed by their absolute path.
 
523
The modules are added automatically by :py:func:`waflib.Context.load_module`
 
524
"""
 
525
 
 
526
def load_module(path):
 
527
        """
 
528
        Load a source file as a python module.
 
529
 
 
530
        :param path: file path
 
531
        :type path: string
 
532
        :return: Loaded Python module
 
533
        :rtype: module
 
534
        """
 
535
        try:
 
536
                return cache_modules[path]
 
537
        except KeyError:
 
538
                pass
 
539
 
 
540
        module = imp.new_module(WSCRIPT_FILE)
 
541
        try:
 
542
                code = Utils.readf(path, m='rU')
 
543
        except (IOError, OSError):
 
544
                raise Errors.WafError('Could not read the file %r' % path)
 
545
 
 
546
        module_dir = os.path.dirname(path)
 
547
        sys.path.insert(0, module_dir)
 
548
 
 
549
        exec(compile(code, path, 'exec'), module.__dict__)
 
550
        sys.path.remove(module_dir)
 
551
 
 
552
        cache_modules[path] = module
 
553
 
 
554
        return module
 
555
 
 
556
def load_tool(tool, tooldir=None):
 
557
        """
 
558
        Import a Waf tool (python module), and store it in the dict :py:const:`waflib.Context.Context.tools`
 
559
 
 
560
        :type  tool: string
 
561
        :param tool: Name of the tool
 
562
        :type  tooldir: list
 
563
        :param tooldir: List of directories to search for the tool module
 
564
        """
 
565
        tool = tool.replace('++', 'xx')
 
566
        tool = tool.replace('java', 'javaw')
 
567
        tool = tool.replace('compiler_cc', 'compiler_c')
 
568
 
 
569
        if tooldir:
 
570
                assert isinstance(tooldir, list)
 
571
                sys.path = tooldir + sys.path
 
572
                try:
 
573
                        __import__(tool)
 
574
                        ret = sys.modules[tool]
 
575
                        Context.tools[tool] = ret
 
576
                        return ret
 
577
                finally:
 
578
                        for d in tooldir:
 
579
                                sys.path.remove(d)
 
580
        else:
 
581
                global waf_dir
 
582
                try:
 
583
                        os.stat(os.path.join(waf_dir, 'waflib', 'extras', tool + '.py'))
 
584
                        d = 'waflib.extras.%s' % tool
 
585
                except:
 
586
                        try:
 
587
                                os.stat(os.path.join(waf_dir, 'waflib', 'Tools', tool + '.py'))
 
588
                                d = 'waflib.Tools.%s' % tool
 
589
                        except:
 
590
                                d = tool # user has messed with sys.path
 
591
 
 
592
                __import__(d)
 
593
                ret = sys.modules[d]
 
594
                Context.tools[tool] = ret
 
595
                return ret
 
596