~vcs-imports/kupfer/master-new

« back to all changes in this revision

Viewing changes to waflib/Node.py

  • Committer: Ulrik Sverdrup
  • Date: 2012-02-26 17:50:05 UTC
  • mfrom: (2916.1.5)
  • Revision ID: git-v1:a1d52c4a74cd48e1b673e68977eba58b48928b7f
Merge branch 'full-waf'

* full-waf:
  Update NEWS
  wscript: Use .xz for distribution tarball
  wscript: Clean all .pyc files on distclean
  Update README for Waf being included in the repository and tarball
  Add waf-light and waflib from waf-1.6.11

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-2010 (ita)
 
4
 
 
5
"""
 
6
Node: filesystem structure, contains lists of nodes
 
7
 
 
8
#. Each file/folder is represented by exactly one node.
 
9
 
 
10
#. Some potential class properties are stored on :py:class:`waflib.Build.BuildContext` : nodes to depend on, etc.
 
11
   Unused class members can increase the `.wafpickle` file size sensibly.
 
12
 
 
13
#. Node objects should never be created directly, use
 
14
   the methods :py:func:`Node.make_node` or :py:func:`Node.find_node`
 
15
 
 
16
#. The methods :py:func:`Node.find_resource`, :py:func:`Node.find_dir` :py:func:`Node.find_or_declare` should be
 
17
   used when a build context is present
 
18
 
 
19
#. Each instance of :py:class:`waflib.Context.Context` has a unique :py:class:`Node` subclass.
 
20
   (:py:class:`waflib.Node.Nod3`, see the :py:class:`waflib.Context.Context` initializer). A reference to the context owning a node is held as self.ctx
 
21
"""
 
22
 
 
23
import os, re, sys, shutil
 
24
from waflib import Utils, Errors
 
25
 
 
26
exclude_regs = '''
 
27
**/*~
 
28
**/#*#
 
29
**/.#*
 
30
**/%*%
 
31
**/._*
 
32
**/CVS
 
33
**/CVS/**
 
34
**/.cvsignore
 
35
**/SCCS
 
36
**/SCCS/**
 
37
**/vssver.scc
 
38
**/.svn
 
39
**/.svn/**
 
40
**/BitKeeper
 
41
**/.git
 
42
**/.git/**
 
43
**/.gitignore
 
44
**/.bzr
 
45
**/.bzrignore
 
46
**/.bzr/**
 
47
**/.hg
 
48
**/.hg/**
 
49
**/_MTN
 
50
**/_MTN/**
 
51
**/.arch-ids
 
52
**/{arch}
 
53
**/_darcs
 
54
**/_darcs/**
 
55
**/.DS_Store'''
 
56
"""
 
57
Ant patterns for files and folders to exclude while doing the
 
58
recursive traversal in :py:meth:`waflib.Node.Node.ant_glob`
 
59
"""
 
60
 
 
61
# TODO optimize split_path by performing a replacement when unpacking?
 
62
 
 
63
def split_path(path):
 
64
        """
 
65
        Split a path by os.sep (This is not os.path.split)
 
66
 
 
67
        :param path: path to split
 
68
        :type path: string
 
69
        :rtype: list of string
 
70
        :return: the path, split
 
71
        """
 
72
        return path.split('/')
 
73
 
 
74
def split_path_cygwin(path):
 
75
        if path.startswith('//'):
 
76
                ret = path.split('/')[2:]
 
77
                ret[0] = '/' + ret[0]
 
78
                return ret
 
79
        return path.split('/')
 
80
 
 
81
re_sp = re.compile('[/\\\\]')
 
82
def split_path_win32(path):
 
83
        if path.startswith('\\\\'):
 
84
                ret = re.split(re_sp, path)[2:]
 
85
                ret[0] = '\\' + ret[0]
 
86
                return ret
 
87
        return re.split(re_sp, path)
 
88
 
 
89
if sys.platform == 'cygwin':
 
90
        split_path = split_path_cygwin
 
91
elif Utils.is_win32:
 
92
        split_path = split_path_win32
 
93
 
 
94
class Node(object):
 
95
        """
 
96
        This class is organized in two parts
 
97
 
 
98
        * The basic methods meant for filesystem access (compute paths, create folders, etc)
 
99
        * The methods bound to a :py:class:`waflib.Build.BuildContext` (require ``bld.srcnode`` and ``bld.bldnode``)
 
100
 
 
101
        The Node objects are not thread safe in any way.
 
102
        """
 
103
 
 
104
        __slots__ = ('name', 'sig', 'children', 'parent', 'cache_abspath', 'cache_isdir')
 
105
        def __init__(self, name, parent):
 
106
                self.name = name
 
107
                self.parent = parent
 
108
 
 
109
                if parent:
 
110
                        if name in parent.children:
 
111
                                raise Errors.WafError('node %s exists in the parent files %r already' % (name, parent))
 
112
                        parent.children[name] = self
 
113
 
 
114
        def __setstate__(self, data):
 
115
                "Deserializes from data"
 
116
                self.name = data[0]
 
117
                self.parent = data[1]
 
118
                if data[2] is not None:
 
119
                        self.children = data[2]
 
120
                if data[3] is not None:
 
121
                        self.sig = data[3]
 
122
 
 
123
        def __getstate__(self):
 
124
                "Serialize the node info"
 
125
                return (self.name, self.parent, getattr(self, 'children', None), getattr(self, 'sig', None))
 
126
 
 
127
        def __str__(self):
 
128
                "String representation (name), for debugging purposes"
 
129
                return self.name
 
130
 
 
131
        def __repr__(self):
 
132
                "String representation (abspath), for debugging purposes"
 
133
                return self.abspath()
 
134
 
 
135
        def __hash__(self):
 
136
                "Node hash, used for storage in dicts. This hash is not persistent."
 
137
                return id(self)
 
138
 
 
139
        def __eq__(self, node):
 
140
                "Node comparison, based on the IDs"
 
141
                return id(self) == id(node)
 
142
 
 
143
        def __copy__(self):
 
144
                "Implemented to prevent nodes from being copied (raises an exception)"
 
145
                raise Errors.WafError('nodes are not supposed to be copied')
 
146
 
 
147
        def read(self, flags='r'):
 
148
                """
 
149
                Return the contents of the file represented by this node::
 
150
 
 
151
                        def build(bld):
 
152
                                bld.path.find_node('wscript').read()
 
153
 
 
154
                :type  fname: string
 
155
                :param fname: Path to file
 
156
                :type  m: string
 
157
                :param m: Open mode
 
158
                :rtype: string
 
159
                :return: File contents
 
160
                """
 
161
                return Utils.readf(self.abspath(), flags)
 
162
 
 
163
        def write(self, data, flags='w'):
 
164
                """
 
165
                Write some text to the physical file represented by this node::
 
166
 
 
167
                        def build(bld):
 
168
                                bld.path.make_node('foo.txt').write('Hello, world!')
 
169
 
 
170
                :type  data: string
 
171
                :param data: data to write
 
172
                :type  flags: string
 
173
                :param flags: Write mode
 
174
                """
 
175
                f = None
 
176
                try:
 
177
                        f = open(self.abspath(), flags)
 
178
                        f.write(data)
 
179
                finally:
 
180
                        if f:
 
181
                                f.close()
 
182
 
 
183
        def chmod(self, val):
 
184
                """
 
185
                Change file/dir permissions::
 
186
 
 
187
                        def build(bld):
 
188
                                bld.path.chmod(493) # 0755
 
189
                """
 
190
                os.chmod(self.abspath(), val)
 
191
 
 
192
        def delete(self):
 
193
                """Delete the file/folder physically (but not the node)"""
 
194
                try:
 
195
                        if getattr(self, 'children', None):
 
196
                                shutil.rmtree(self.abspath())
 
197
                        else:
 
198
                                os.unlink(self.abspath())
 
199
                except:
 
200
                        pass
 
201
 
 
202
                try:
 
203
                        delattr(self, 'children')
 
204
                except:
 
205
                        pass
 
206
 
 
207
        def suffix(self):
 
208
                """Return the file extension"""
 
209
                k = max(0, self.name.rfind('.'))
 
210
                return self.name[k:]
 
211
 
 
212
        def height(self):
 
213
                """Depth in the folder hierarchy from the filesystem root or from all the file drives"""
 
214
                d = self
 
215
                val = -1
 
216
                while d:
 
217
                        d = d.parent
 
218
                        val += 1
 
219
                return val
 
220
 
 
221
        def listdir(self):
 
222
                """List the folder contents"""
 
223
                lst = Utils.listdir(self.abspath())
 
224
                lst.sort()
 
225
                return lst
 
226
 
 
227
        def mkdir(self):
 
228
                """
 
229
                Create a folder represented by this node, creating intermediate nodes as needed
 
230
                An exception will be raised only when the folder cannot possibly exist there
 
231
                """
 
232
                if getattr(self, 'cache_isdir', None):
 
233
                        return
 
234
 
 
235
                try:
 
236
                        self.parent.mkdir()
 
237
                except:
 
238
                        pass
 
239
 
 
240
                if self.name:
 
241
                        try:
 
242
                                os.makedirs(self.abspath())
 
243
                        except OSError:
 
244
                                pass
 
245
 
 
246
                        if not os.path.isdir(self.abspath()):
 
247
                                raise Errors.WafError('Could not create the directory %s' % self.abspath())
 
248
 
 
249
                        try:
 
250
                                self.children
 
251
                        except:
 
252
                                self.children = {}
 
253
 
 
254
                self.cache_isdir = True
 
255
 
 
256
        def find_node(self, lst):
 
257
                """
 
258
                Find a node on the file system (files or folders), create intermediate nodes as needed
 
259
 
 
260
                :param lst: path
 
261
                :type lst: string or list of string
 
262
                """
 
263
 
 
264
                if isinstance(lst, str):
 
265
                        lst = [x for x in split_path(lst) if x and x != '.']
 
266
 
 
267
                cur = self
 
268
                for x in lst:
 
269
                        if x == '..':
 
270
                                cur = cur.parent or cur
 
271
                                continue
 
272
 
 
273
                        try:
 
274
                                if x in cur.children:
 
275
                                        cur = cur.children[x]
 
276
                                        continue
 
277
                        except:
 
278
                                cur.children = {}
 
279
 
 
280
                        # optimistic: create the node first then look if it was correct to do so
 
281
                        cur = self.__class__(x, cur)
 
282
                        try:
 
283
                                os.stat(cur.abspath())
 
284
                        except:
 
285
                                del cur.parent.children[x]
 
286
                                return None
 
287
 
 
288
                ret = cur
 
289
 
 
290
                try:
 
291
                        os.stat(ret.abspath())
 
292
                except:
 
293
                        del ret.parent.children[ret.name]
 
294
                        return None
 
295
 
 
296
                try:
 
297
                        while not getattr(cur.parent, 'cache_isdir', None):
 
298
                                cur = cur.parent
 
299
                                cur.cache_isdir = True
 
300
                except AttributeError:
 
301
                        pass
 
302
 
 
303
                return ret
 
304
 
 
305
        def make_node(self, lst):
 
306
                """
 
307
                Find or create a node without looking on the filesystem
 
308
 
 
309
                :param lst: path
 
310
                :type lst: string or list of string
 
311
                """
 
312
                if isinstance(lst, str):
 
313
                        lst = [x for x in split_path(lst) if x and x != '.']
 
314
 
 
315
                cur = self
 
316
                for x in lst:
 
317
                        if x == '..':
 
318
                                cur = cur.parent or cur
 
319
                                continue
 
320
 
 
321
                        if getattr(cur, 'children', {}):
 
322
                                if x in cur.children:
 
323
                                        cur = cur.children[x]
 
324
                                        continue
 
325
                        else:
 
326
                                cur.children = {}
 
327
                        cur = self.__class__(x, cur)
 
328
                return cur
 
329
 
 
330
        def search(self, lst):
 
331
                """
 
332
                Search for a node without looking on the filesystem
 
333
 
 
334
                :param lst: path
 
335
                :type lst: string or list of string
 
336
                """
 
337
                if isinstance(lst, str):
 
338
                        lst = [x for x in split_path(lst) if x and x != '.']
 
339
 
 
340
                cur = self
 
341
                try:
 
342
                        for x in lst:
 
343
                                if x == '..':
 
344
                                        cur = cur.parent or cur
 
345
                                else:
 
346
                                        cur = cur.children[x]
 
347
                        return cur
 
348
                except:
 
349
                        pass
 
350
 
 
351
        def path_from(self, node):
 
352
                """
 
353
                Path of this node seen from the other::
 
354
 
 
355
                        def build(bld):
 
356
                                n1 = bld.path.find_node('foo/bar/xyz.txt')
 
357
                                n2 = bld.path.find_node('foo/stuff/')
 
358
                                n1.path_from(n2) # './bar/xyz.txt'
 
359
 
 
360
                :param node: path to use as a reference
 
361
                :type node: :py:class:`waflib.Node.Node`
 
362
                """
 
363
 
 
364
                c1 = self
 
365
                c2 = node
 
366
 
 
367
                c1h = c1.height()
 
368
                c2h = c2.height()
 
369
 
 
370
                lst = []
 
371
                up = 0
 
372
 
 
373
                while c1h > c2h:
 
374
                        lst.append(c1.name)
 
375
                        c1 = c1.parent
 
376
                        c1h -= 1
 
377
 
 
378
                while c2h > c1h:
 
379
                        up += 1
 
380
                        c2 = c2.parent
 
381
                        c2h -= 1
 
382
 
 
383
                while id(c1) != id(c2):
 
384
                        lst.append(c1.name)
 
385
                        up += 1
 
386
 
 
387
                        c1 = c1.parent
 
388
                        c2 = c2.parent
 
389
 
 
390
                for i in range(up):
 
391
                        lst.append('..')
 
392
                lst.reverse()
 
393
                return os.sep.join(lst) or '.'
 
394
 
 
395
        def abspath(self):
 
396
                """
 
397
                Absolute path. A cache is kept in the context as ``cache_node_abspath``
 
398
                """
 
399
                try:
 
400
                        return self.cache_abspath
 
401
                except:
 
402
                        pass
 
403
                # think twice before touching this (performance + complexity + correctness)
 
404
 
 
405
                if os.sep == '/':
 
406
                        if not self.parent:
 
407
                                val = os.sep
 
408
                        elif not self.parent.name:
 
409
                                val = os.sep + self.name
 
410
                        else:
 
411
                                val = self.parent.abspath() + os.sep + self.name
 
412
                else:
 
413
                        if not self.parent:
 
414
                                val = ''
 
415
                        elif not self.parent.name:
 
416
                                val = self.name + os.sep
 
417
                        else:
 
418
                                val = self.parent.abspath().rstrip(os.sep) + os.sep + self.name
 
419
 
 
420
                self.cache_abspath = val
 
421
                return val
 
422
 
 
423
        def is_child_of(self, node):
 
424
                """
 
425
                Does this node belong to the subtree node?::
 
426
 
 
427
                        def build(bld):
 
428
                                node = bld.path.find_node('wscript')
 
429
                                node.is_child_of(bld.path) # True
 
430
 
 
431
                :param node: path to use as a reference
 
432
                :type node: :py:class:`waflib.Node.Node`
 
433
                """
 
434
                p = self
 
435
                diff = self.height() - node.height()
 
436
                while diff > 0:
 
437
                        diff -= 1
 
438
                        p = p.parent
 
439
                return id(p) == id(node)
 
440
 
 
441
        def ant_iter(self, accept=None, maxdepth=25, pats=[], dir=False, src=True, remove=True):
 
442
                """
 
443
                Semi-private and recursive method used by ant_glob.
 
444
 
 
445
                :param accept: function used for accepting/rejecting a node, returns the patterns that can be still accepted in recursion
 
446
                :type accept: function
 
447
                :param maxdepth: maximum depth in the filesystem (25)
 
448
                :type maxdepth: int
 
449
                :param pats: list of patterns to accept and list of patterns to exclude
 
450
                :type pats: tuple
 
451
                :param dir: return folders too (False by default)
 
452
                :type dir: bool
 
453
                :param src: return files (True by default)
 
454
                :type src: bool
 
455
                :param remove: remove files/folders that do not exist (True by default)
 
456
                :type remove: bool
 
457
                """
 
458
                dircont = self.listdir()
 
459
                dircont.sort()
 
460
 
 
461
                try:
 
462
                        lst = set(self.children.keys())
 
463
                        if remove:
 
464
                                for x in lst - set(dircont):
 
465
                                        del self.children[x]
 
466
                except:
 
467
                        self.children = {}
 
468
 
 
469
                for name in dircont:
 
470
                        npats = accept(name, pats)
 
471
                        if npats and npats[0]:
 
472
                                accepted = [] in npats[0]
 
473
 
 
474
                                node = self.make_node([name])
 
475
 
 
476
                                isdir = os.path.isdir(node.abspath())
 
477
                                if accepted:
 
478
                                        if isdir:
 
479
                                                if dir:
 
480
                                                        yield node
 
481
                                        else:
 
482
                                                if src:
 
483
                                                        yield node
 
484
 
 
485
                                if getattr(node, 'cache_isdir', None) or isdir:
 
486
                                        node.cache_isdir = True
 
487
                                        if maxdepth:
 
488
                                                for k in node.ant_iter(accept=accept, maxdepth=maxdepth - 1, pats=npats, dir=dir, src=src, remove=remove):
 
489
                                                        yield k
 
490
                raise StopIteration
 
491
 
 
492
        def ant_glob(self, *k, **kw):
 
493
                """
 
494
                This method is used for finding files across folders. It behaves like ant patterns:
 
495
 
 
496
                * ``**/*`` find all files recursively
 
497
                * ``**/*.class`` find all files ending by .class
 
498
                * ``..`` find files having two dot characters
 
499
 
 
500
                For example::
 
501
 
 
502
                        def configure(cfg):
 
503
                                cfg.path.ant_glob('**/*.cpp') # find all .cpp files
 
504
                                cfg.root.ant_glob('etc/*.txt') # using the filesystem root can be slow
 
505
                                cfg.path.ant_glob('*.cpp', excl=['*.c'], src=True, dir=False)
 
506
 
 
507
                For more information see http://ant.apache.org/manual/dirtasks.html
 
508
 
 
509
                The nodes that correspond to files and folders that do not exist will be removed. To prevent this
 
510
                behaviour, pass 'remove=False'
 
511
 
 
512
                :param incl: ant patterns or list of patterns to include
 
513
                :type incl: string or list of strings
 
514
                :param excl: ant patterns or list of patterns to exclude
 
515
                :type excl: string or list of strings
 
516
                :param dir: return folders too (False by default)
 
517
                :type dir: bool
 
518
                :param src: return files (True by default)
 
519
                :type src: bool
 
520
                :param remove: remove files/folders that do not exist (True by default)
 
521
                :type remove: bool
 
522
                :param maxdepth: maximum depth of recursion
 
523
                :type maxdepth: int
 
524
                """
 
525
 
 
526
                src = kw.get('src', True)
 
527
                dir = kw.get('dir', False)
 
528
 
 
529
                excl = kw.get('excl', exclude_regs)
 
530
                incl = k and k[0] or kw.get('incl', '**')
 
531
 
 
532
                def to_pat(s):
 
533
                        lst = Utils.to_list(s)
 
534
                        ret = []
 
535
                        for x in lst:
 
536
                                x = x.replace('\\', '/').replace('//', '/')
 
537
                                if x.endswith('/'):
 
538
                                        x += '**'
 
539
                                lst2 = x.split('/')
 
540
                                accu = []
 
541
                                for k in lst2:
 
542
                                        if k == '**':
 
543
                                                accu.append(k)
 
544
                                        else:
 
545
                                                k = k.replace('.', '[.]').replace('*','.*').replace('?', '.').replace('+', '\\+')
 
546
                                                k = '^%s$' % k
 
547
                                                try:
 
548
                                                        #print "pattern", k
 
549
                                                        accu.append(re.compile(k))
 
550
                                                except Exception as e:
 
551
                                                        raise Errors.WafError("Invalid pattern: %s" % k, e)
 
552
                                ret.append(accu)
 
553
                        return ret
 
554
 
 
555
                def filtre(name, nn):
 
556
                        ret = []
 
557
                        for lst in nn:
 
558
                                if not lst:
 
559
                                        pass
 
560
                                elif lst[0] == '**':
 
561
                                        ret.append(lst)
 
562
                                        if len(lst) > 1:
 
563
                                                if lst[1].match(name):
 
564
                                                        ret.append(lst[2:])
 
565
                                        else:
 
566
                                                ret.append([])
 
567
                                elif lst[0].match(name):
 
568
                                        ret.append(lst[1:])
 
569
                        return ret
 
570
 
 
571
                def accept(name, pats):
 
572
                        nacc = filtre(name, pats[0])
 
573
                        nrej = filtre(name, pats[1])
 
574
                        if [] in nrej:
 
575
                                nacc = []
 
576
                        return [nacc, nrej]
 
577
 
 
578
                ret = [x for x in self.ant_iter(accept=accept, pats=[to_pat(incl), to_pat(excl)], maxdepth=25, dir=dir, src=src, remove=kw.get('remove', True))]
 
579
                if kw.get('flat', False):
 
580
                        return ' '.join([x.path_from(self) for x in ret])
 
581
 
 
582
                return ret
 
583
 
 
584
        def find_nodes(self, find_dirs=True, find_files=True, match_fun=lambda x: True):
 
585
                # FIXME not part of the stable API: find_node vs find_nodes? consistency with argument names on other functions?
 
586
                x = """
 
587
                Recursively finds nodes::
 
588
 
 
589
                        def configure(cnf):
 
590
                                cnf.find_nodes()
 
591
 
 
592
                :param find_dirs: whether to return directories
 
593
                :param find_files: whether to return files
 
594
                :param match_fun: matching function, taking a node as parameter
 
595
                :rtype generator
 
596
                :return: a generator that iterates over all the requested files
 
597
                """
 
598
                files = self.listdir()
 
599
                for f in files:
 
600
                        node = self.make_node([f])
 
601
                        if os.path.isdir(node.abspath()):
 
602
                                if find_dirs and match_fun(node):
 
603
                                        yield node
 
604
                                gen = node.find_nodes(find_dirs, find_files, match_fun)
 
605
                                for g in gen:
 
606
                                        yield g
 
607
                        else:
 
608
                                if find_files and match_fun(node):
 
609
                                        yield node
 
610
 
 
611
 
 
612
        # --------------------------------------------------------------------------------
 
613
        # the following methods require the source/build folders (bld.srcnode/bld.bldnode)
 
614
        # using a subclass is a possibility, but is that really necessary?
 
615
        # --------------------------------------------------------------------------------
 
616
 
 
617
        def is_src(self):
 
618
                """
 
619
                True if the node is below the source directory
 
620
                note: !is_src does not imply is_bld()
 
621
 
 
622
                :rtype: bool
 
623
                """
 
624
                cur = self
 
625
                x = id(self.ctx.srcnode)
 
626
                y = id(self.ctx.bldnode)
 
627
                while cur.parent:
 
628
                        if id(cur) == y:
 
629
                                return False
 
630
                        if id(cur) == x:
 
631
                                return True
 
632
                        cur = cur.parent
 
633
                return False
 
634
 
 
635
        def is_bld(self):
 
636
                """
 
637
                True if the node is below the build directory
 
638
                note: !is_bld does not imply is_src
 
639
 
 
640
                :rtype: bool
 
641
                """
 
642
                cur = self
 
643
                y = id(self.ctx.bldnode)
 
644
                while cur.parent:
 
645
                        if id(cur) == y:
 
646
                                return True
 
647
                        cur = cur.parent
 
648
                return False
 
649
 
 
650
        def get_src(self):
 
651
                """
 
652
                Return the equivalent src node (or self if not possible)
 
653
 
 
654
                :rtype: :py:class:`waflib.Node.Node`
 
655
                """
 
656
                cur = self
 
657
                x = id(self.ctx.srcnode)
 
658
                y = id(self.ctx.bldnode)
 
659
                lst = []
 
660
                while cur.parent:
 
661
                        if id(cur) == y:
 
662
                                lst.reverse()
 
663
                                return self.ctx.srcnode.make_node(lst)
 
664
                        if id(cur) == x:
 
665
                                return self
 
666
                        lst.append(cur.name)
 
667
                        cur = cur.parent
 
668
                return self
 
669
 
 
670
        def get_bld(self):
 
671
                """
 
672
                Return the equivalent bld node (or self if not possible)
 
673
 
 
674
                :rtype: :py:class:`waflib.Node.Node`
 
675
                """
 
676
                cur = self
 
677
                x = id(self.ctx.srcnode)
 
678
                y = id(self.ctx.bldnode)
 
679
                lst = []
 
680
                while cur.parent:
 
681
                        if id(cur) == y:
 
682
                                return self
 
683
                        if id(cur) == x:
 
684
                                lst.reverse()
 
685
                                return self.ctx.bldnode.make_node(lst)
 
686
                        lst.append(cur.name)
 
687
                        cur = cur.parent
 
688
                # the file is external to the current project, make a fake root in the current build directory
 
689
                lst.reverse()
 
690
                if lst and Utils.is_win32 and len(lst[0]) == 2 and lst[0].endswith(':'):
 
691
                        lst[0] = lst[0][0]
 
692
                return self.ctx.bldnode.make_node(['__root__'] + lst)
 
693
 
 
694
        def find_resource(self, lst):
 
695
                """
 
696
                Try to find a declared build node or a source file
 
697
 
 
698
                :param lst: path
 
699
                :type lst: string or list of string
 
700
                """
 
701
                if isinstance(lst, str):
 
702
                        lst = [x for x in split_path(lst) if x and x != '.']
 
703
 
 
704
                node = self.get_bld().search(lst)
 
705
                if not node:
 
706
                        self = self.get_src()
 
707
                        node = self.find_node(lst)
 
708
                try:
 
709
                        pat = node.abspath()
 
710
                        if os.path.isdir(pat):
 
711
                                return None
 
712
                except:
 
713
                        pass
 
714
                return node
 
715
 
 
716
        def find_or_declare(self, lst):
 
717
                """
 
718
                if 'self' is in build directory, try to return an existing node
 
719
                if no node is found, go to the source directory
 
720
                try to find an existing node in the source directory
 
721
                if no node is found, create it in the build directory
 
722
 
 
723
                :param lst: path
 
724
                :type lst: string or list of string
 
725
                """
 
726
                if isinstance(lst, str):
 
727
                        lst = [x for x in split_path(lst) if x and x != '.']
 
728
 
 
729
                node = self.get_bld().search(lst)
 
730
                if node:
 
731
                        if not os.path.isfile(node.abspath()):
 
732
                                node.sig = None
 
733
                                try:
 
734
                                        node.parent.mkdir()
 
735
                                except:
 
736
                                        pass
 
737
                        return node
 
738
                self = self.get_src()
 
739
                node = self.find_node(lst)
 
740
                if node:
 
741
                        if not os.path.isfile(node.abspath()):
 
742
                                node.sig = None
 
743
                                try:
 
744
                                        node.parent.mkdir()
 
745
                                except:
 
746
                                        pass
 
747
                        return node
 
748
                node = self.get_bld().make_node(lst)
 
749
                node.parent.mkdir()
 
750
                return node
 
751
 
 
752
        def find_dir(self, lst):
 
753
                """
 
754
                Search for a folder in the filesystem
 
755
 
 
756
                :param lst: path
 
757
                :type lst: string or list of string
 
758
                """
 
759
                if isinstance(lst, str):
 
760
                        lst = [x for x in split_path(lst) if x and x != '.']
 
761
 
 
762
                node = self.find_node(lst)
 
763
                try:
 
764
                        if not os.path.isdir(node.abspath()):
 
765
                                return None
 
766
                except (OSError, AttributeError):
 
767
                        # the node might be None, and raise an AttributeError
 
768
                        return None
 
769
                return node
 
770
 
 
771
        # helpers for building things
 
772
        def change_ext(self, ext, ext_in=None):
 
773
                """
 
774
                :return: A build node of the same path, but with a different extension
 
775
                :rtype: :py:class:`waflib.Node.Node`
 
776
                """
 
777
                name = self.name
 
778
                if ext_in is None:
 
779
                        k = name.rfind('.')
 
780
                        if k >= 0:
 
781
                                name = name[:k] + ext
 
782
                        else:
 
783
                                name = name + ext
 
784
                else:
 
785
                        name = name[:- len(ext_in)] + ext
 
786
 
 
787
                return self.parent.find_or_declare([name])
 
788
 
 
789
        def nice_path(self, env=None):
 
790
                """
 
791
                Return the path seen from the launch directory. It is often used for printing nodes in the console to open
 
792
                files easily.
 
793
 
 
794
                :param env: unused, left for compatibility with waf 1.5
 
795
                """
 
796
                return self.path_from(self.ctx.launch_node())
 
797
 
 
798
        def bldpath(self):
 
799
                "Path seen from the build directory default/src/foo.cpp"
 
800
                return self.path_from(self.ctx.bldnode)
 
801
 
 
802
        def srcpath(self):
 
803
                "Path seen from the source directory ../src/foo.cpp"
 
804
                return self.path_from(self.ctx.srcnode)
 
805
 
 
806
        def relpath(self):
 
807
                "If a file in the build directory, bldpath, else srcpath"
 
808
                cur = self
 
809
                x = id(self.ctx.bldnode)
 
810
                while cur.parent:
 
811
                        if id(cur) == x:
 
812
                                return self.bldpath()
 
813
                        cur = cur.parent
 
814
                return self.srcpath()
 
815
 
 
816
        def bld_dir(self):
 
817
                "Build path without the file name"
 
818
                return self.parent.bldpath()
 
819
 
 
820
        def bld_base(self):
 
821
                "Build path without the extension: src/dir/foo(.cpp)"
 
822
                s = os.path.splitext(self.name)[0]
 
823
                return self.bld_dir() + os.sep + s
 
824
 
 
825
        def get_bld_sig(self):
 
826
                """
 
827
                Node signature, assuming the file is in the build directory
 
828
                """
 
829
                try:
 
830
                        ret = self.ctx.hash_cache[id(self)]
 
831
                except KeyError:
 
832
                        pass
 
833
                except AttributeError:
 
834
                        self.ctx.hash_cache = {}
 
835
                else:
 
836
                        return ret
 
837
 
 
838
                if not self.is_bld() or self.ctx.bldnode is self.ctx.srcnode:
 
839
                        self.sig = Utils.h_file(self.abspath())
 
840
                self.ctx.hash_cache[id(self)] = ret = self.sig
 
841
                return ret
 
842
 
 
843
pickle_lock = Utils.threading.Lock()
 
844
"""Lock mandatory for thread-safe node serialization"""
 
845
 
 
846
class Nod3(Node):
 
847
        """Mandatory subclass for thread-safe node serialization"""
 
848
        pass # do not remove
 
849
 
 
850