Update changelog
[jack2.git] / waflib / Node.py
blob4ac1ea8a0b8abf5debd991ccdff70eae0ae93e41
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005-2018 (ita)
5 """
6 Node: filesystem structure
8 #. Each file/folder is represented by exactly one node.
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.
13 #. Node objects should never be created directly, use
14 the methods :py:func:`Node.make_node` or :py:func:`Node.find_node` for the low-level operations
16 #. The methods :py:func:`Node.find_resource`, :py:func:`Node.find_dir` :py:func:`Node.find_or_declare` must be
17 used when a build context is present
19 #. Each instance of :py:class:`waflib.Context.Context` has a unique :py:class:`Node` subclass required for serialization.
20 (:py:class:`waflib.Node.Nod3`, see the :py:class:`waflib.Context.Context` initializer). A reference to the context
21 owning a node is held as *self.ctx*
22 """
24 import os, re, sys, shutil
25 from waflib import Utils, Errors
27 exclude_regs = '''
28 **/*~
29 **/#*#
30 **/.#*
31 **/%*%
32 **/._*
33 **/*.swp
34 **/CVS
35 **/CVS/**
36 **/.cvsignore
37 **/SCCS
38 **/SCCS/**
39 **/vssver.scc
40 **/.svn
41 **/.svn/**
42 **/BitKeeper
43 **/.git
44 **/.git/**
45 **/.gitignore
46 **/.bzr
47 **/.bzrignore
48 **/.bzr/**
49 **/.hg
50 **/.hg/**
51 **/_MTN
52 **/_MTN/**
53 **/.arch-ids
54 **/{arch}
55 **/_darcs
56 **/_darcs/**
57 **/.intlcache
58 **/.DS_Store'''
59 """
60 Ant patterns for files and folders to exclude while doing the
61 recursive traversal in :py:meth:`waflib.Node.Node.ant_glob`
62 """
64 def ant_matcher(s, ignorecase):
65 reflags = re.I if ignorecase else 0
66 ret = []
67 for x in Utils.to_list(s):
68 x = x.replace('\\', '/').replace('//', '/')
69 if x.endswith('/'):
70 x += '**'
71 accu = []
72 for k in x.split('/'):
73 if k == '**':
74 accu.append(k)
75 else:
76 k = k.replace('.', '[.]').replace('*','.*').replace('?', '.').replace('+', '\\+')
77 k = '^%s$' % k
78 try:
79 exp = re.compile(k, flags=reflags)
80 except Exception as e:
81 raise Errors.WafError('Invalid pattern: %s' % k, e)
82 else:
83 accu.append(exp)
84 ret.append(accu)
85 return ret
87 def ant_sub_filter(name, nn):
88 ret = []
89 for lst in nn:
90 if not lst:
91 pass
92 elif lst[0] == '**':
93 ret.append(lst)
94 if len(lst) > 1:
95 if lst[1].match(name):
96 ret.append(lst[2:])
97 else:
98 ret.append([])
99 elif lst[0].match(name):
100 ret.append(lst[1:])
101 return ret
103 def ant_sub_matcher(name, pats):
104 nacc = ant_sub_filter(name, pats[0])
105 nrej = ant_sub_filter(name, pats[1])
106 if [] in nrej:
107 nacc = []
108 return [nacc, nrej]
110 class Node(object):
112 This class is organized in two parts:
114 * The basic methods meant for filesystem access (compute paths, create folders, etc)
115 * The methods bound to a :py:class:`waflib.Build.BuildContext` (require ``bld.srcnode`` and ``bld.bldnode``)
118 dict_class = dict
120 Subclasses can provide a dict class to enable case insensitivity for example.
123 __slots__ = ('name', 'parent', 'children', 'cache_abspath', 'cache_isdir')
124 def __init__(self, name, parent):
126 .. note:: Use :py:func:`Node.make_node` or :py:func:`Node.find_node` instead of calling this constructor
128 self.name = name
129 self.parent = parent
130 if parent:
131 if name in parent.children:
132 raise Errors.WafError('node %s exists in the parent files %r already' % (name, parent))
133 parent.children[name] = self
135 def __setstate__(self, data):
136 "Deserializes node information, used for persistence"
137 self.name = data[0]
138 self.parent = data[1]
139 if data[2] is not None:
140 # Issue 1480
141 self.children = self.dict_class(data[2])
143 def __getstate__(self):
144 "Serializes node information, used for persistence"
145 return (self.name, self.parent, getattr(self, 'children', None))
147 def __str__(self):
149 String representation (abspath), for debugging purposes
151 :rtype: string
153 return self.abspath()
155 def __repr__(self):
157 String representation (abspath), for debugging purposes
159 :rtype: string
161 return self.abspath()
163 def __copy__(self):
165 Provided to prevent nodes from being copied
167 :raises: :py:class:`waflib.Errors.WafError`
169 raise Errors.WafError('nodes are not supposed to be copied')
171 def read(self, flags='r', encoding='latin-1'):
173 Reads and returns the contents of the file represented by this node, see :py:func:`waflib.Utils.readf`::
175 def build(bld):
176 bld.path.find_node('wscript').read()
178 :param flags: Open mode
179 :type flags: string
180 :param encoding: encoding value for Python3
181 :type encoding: string
182 :rtype: string or bytes
183 :return: File contents
185 return Utils.readf(self.abspath(), flags, encoding)
187 def write(self, data, flags='w', encoding='latin-1'):
189 Writes data to the file represented by this node, see :py:func:`waflib.Utils.writef`::
191 def build(bld):
192 bld.path.make_node('foo.txt').write('Hello, world!')
194 :param data: data to write
195 :type data: string
196 :param flags: Write mode
197 :type flags: string
198 :param encoding: encoding value for Python3
199 :type encoding: string
201 Utils.writef(self.abspath(), data, flags, encoding)
203 def read_json(self, convert=True, encoding='utf-8'):
205 Reads and parses the contents of this node as JSON (Python ≥ 2.6)::
207 def build(bld):
208 bld.path.find_node('abc.json').read_json()
210 Note that this by default automatically decodes unicode strings on Python2, unlike what the Python JSON module does.
212 :type convert: boolean
213 :param convert: Prevents decoding of unicode strings on Python2
214 :type encoding: string
215 :param encoding: The encoding of the file to read. This default to UTF8 as per the JSON standard
216 :rtype: object
217 :return: Parsed file contents
219 import json # Python 2.6 and up
220 object_pairs_hook = None
221 if convert and sys.hexversion < 0x3000000:
222 try:
223 _type = unicode
224 except NameError:
225 _type = str
227 def convert(value):
228 if isinstance(value, list):
229 return [convert(element) for element in value]
230 elif isinstance(value, _type):
231 return str(value)
232 else:
233 return value
235 def object_pairs(pairs):
236 return dict((str(pair[0]), convert(pair[1])) for pair in pairs)
238 object_pairs_hook = object_pairs
240 return json.loads(self.read(encoding=encoding), object_pairs_hook=object_pairs_hook)
242 def write_json(self, data, pretty=True):
244 Writes a python object as JSON to disk (Python ≥ 2.6) as UTF-8 data (JSON standard)::
246 def build(bld):
247 bld.path.find_node('xyz.json').write_json(199)
249 :type data: object
250 :param data: The data to write to disk
251 :type pretty: boolean
252 :param pretty: Determines if the JSON will be nicely space separated
254 import json # Python 2.6 and up
255 indent = 2
256 separators = (',', ': ')
257 sort_keys = pretty
258 newline = os.linesep
259 if not pretty:
260 indent = None
261 separators = (',', ':')
262 newline = ''
263 output = json.dumps(data, indent=indent, separators=separators, sort_keys=sort_keys) + newline
264 self.write(output, encoding='utf-8')
266 def exists(self):
268 Returns whether the Node is present on the filesystem
270 :rtype: bool
272 return os.path.exists(self.abspath())
274 def isdir(self):
276 Returns whether the Node represents a folder
278 :rtype: bool
280 return os.path.isdir(self.abspath())
282 def chmod(self, val):
284 Changes the file/dir permissions::
286 def build(bld):
287 bld.path.chmod(493) # 0755
289 os.chmod(self.abspath(), val)
291 def delete(self, evict=True):
293 Removes the file/folder from the filesystem (equivalent to `rm -rf`), and remove this object from the Node tree.
294 Do not use this object after calling this method.
296 try:
297 try:
298 if os.path.isdir(self.abspath()):
299 shutil.rmtree(self.abspath())
300 else:
301 os.remove(self.abspath())
302 except OSError:
303 if os.path.exists(self.abspath()):
304 raise
305 finally:
306 if evict:
307 self.evict()
309 def evict(self):
311 Removes this node from the Node tree
313 del self.parent.children[self.name]
315 def suffix(self):
317 Returns the file rightmost extension, for example `a.b.c.d → .d`
319 :rtype: string
321 k = max(0, self.name.rfind('.'))
322 return self.name[k:]
324 def height(self):
326 Returns the depth in the folder hierarchy from the filesystem root or from all the file drives
328 :returns: filesystem depth
329 :rtype: integer
331 d = self
332 val = -1
333 while d:
334 d = d.parent
335 val += 1
336 return val
338 def listdir(self):
340 Lists the folder contents
342 :returns: list of file/folder names ordered alphabetically
343 :rtype: list of string
345 lst = Utils.listdir(self.abspath())
346 lst.sort()
347 return lst
349 def mkdir(self):
351 Creates a folder represented by this node. Intermediate folders are created as needed.
353 :raises: :py:class:`waflib.Errors.WafError` when the folder is missing
355 if self.isdir():
356 return
358 try:
359 self.parent.mkdir()
360 except OSError:
361 pass
363 if self.name:
364 try:
365 os.makedirs(self.abspath())
366 except OSError:
367 pass
369 if not self.isdir():
370 raise Errors.WafError('Could not create the directory %r' % self)
372 try:
373 self.children
374 except AttributeError:
375 self.children = self.dict_class()
377 def find_node(self, lst):
379 Finds a node on the file system (files or folders), and creates the corresponding Node objects if it exists
381 :param lst: relative path
382 :type lst: string or list of string
383 :returns: The corresponding Node object or None if no entry was found on the filesystem
384 :rtype: :py:class:´waflib.Node.Node´
387 if isinstance(lst, str):
388 lst = [x for x in Utils.split_path(lst) if x and x != '.']
390 if lst and lst[0].startswith('\\\\') and not self.parent:
391 node = self.ctx.root.make_node(lst[0])
392 node.cache_isdir = True
393 return node.find_node(lst[1:])
395 cur = self
396 for x in lst:
397 if x == '..':
398 cur = cur.parent or cur
399 continue
401 try:
402 ch = cur.children
403 except AttributeError:
404 cur.children = self.dict_class()
405 else:
406 try:
407 cur = ch[x]
408 continue
409 except KeyError:
410 pass
412 # optimistic: create the node first then look if it was correct to do so
413 cur = self.__class__(x, cur)
414 if not cur.exists():
415 cur.evict()
416 return None
418 if not cur.exists():
419 cur.evict()
420 return None
422 return cur
424 def make_node(self, lst):
426 Returns or creates a Node object corresponding to the input path without considering the filesystem.
428 :param lst: relative path
429 :type lst: string or list of string
430 :rtype: :py:class:´waflib.Node.Node´
432 if isinstance(lst, str):
433 lst = [x for x in Utils.split_path(lst) if x and x != '.']
435 cur = self
436 for x in lst:
437 if x == '..':
438 cur = cur.parent or cur
439 continue
441 try:
442 cur = cur.children[x]
443 except AttributeError:
444 cur.children = self.dict_class()
445 except KeyError:
446 pass
447 else:
448 continue
449 cur = self.__class__(x, cur)
450 return cur
452 def search_node(self, lst):
454 Returns a Node previously defined in the data structure. The filesystem is not considered.
456 :param lst: relative path
457 :type lst: string or list of string
458 :rtype: :py:class:´waflib.Node.Node´ or None if there is no entry in the Node datastructure
460 if isinstance(lst, str):
461 lst = [x for x in Utils.split_path(lst) if x and x != '.']
463 cur = self
464 for x in lst:
465 if x == '..':
466 cur = cur.parent or cur
467 else:
468 try:
469 cur = cur.children[x]
470 except (AttributeError, KeyError):
471 return None
472 return cur
474 def path_from(self, node):
476 Path of this node seen from the other::
478 def build(bld):
479 n1 = bld.path.find_node('foo/bar/xyz.txt')
480 n2 = bld.path.find_node('foo/stuff/')
481 n1.path_from(n2) # '../bar/xyz.txt'
483 :param node: path to use as a reference
484 :type node: :py:class:`waflib.Node.Node`
485 :returns: a relative path or an absolute one if that is better
486 :rtype: string
488 c1 = self
489 c2 = node
491 c1h = c1.height()
492 c2h = c2.height()
494 lst = []
495 up = 0
497 while c1h > c2h:
498 lst.append(c1.name)
499 c1 = c1.parent
500 c1h -= 1
502 while c2h > c1h:
503 up += 1
504 c2 = c2.parent
505 c2h -= 1
507 while not c1 is c2:
508 lst.append(c1.name)
509 up += 1
511 c1 = c1.parent
512 c2 = c2.parent
514 if c1.parent:
515 lst.extend(['..'] * up)
516 lst.reverse()
517 return os.sep.join(lst) or '.'
518 else:
519 return self.abspath()
521 def abspath(self):
523 Returns the absolute path. A cache is kept in the context as ``cache_node_abspath``
525 :rtype: string
527 try:
528 return self.cache_abspath
529 except AttributeError:
530 pass
531 # think twice before touching this (performance + complexity + correctness)
533 if not self.parent:
534 val = os.sep
535 elif not self.parent.name:
536 val = os.sep + self.name
537 else:
538 val = self.parent.abspath() + os.sep + self.name
539 self.cache_abspath = val
540 return val
542 if Utils.is_win32:
543 def abspath(self):
544 try:
545 return self.cache_abspath
546 except AttributeError:
547 pass
548 if not self.parent:
549 val = ''
550 elif not self.parent.name:
551 val = self.name + os.sep
552 else:
553 val = self.parent.abspath().rstrip(os.sep) + os.sep + self.name
554 self.cache_abspath = val
555 return val
557 def is_child_of(self, node):
559 Returns whether the object belongs to a subtree of the input node::
561 def build(bld):
562 node = bld.path.find_node('wscript')
563 node.is_child_of(bld.path) # True
565 :param node: path to use as a reference
566 :type node: :py:class:`waflib.Node.Node`
567 :rtype: bool
569 p = self
570 diff = self.height() - node.height()
571 while diff > 0:
572 diff -= 1
573 p = p.parent
574 return p is node
576 def ant_iter(self, accept=None, maxdepth=25, pats=[], dir=False, src=True, remove=True, quiet=False):
578 Recursive method used by :py:meth:`waflib.Node.ant_glob`.
580 :param accept: function used for accepting/rejecting a node, returns the patterns that can be still accepted in recursion
581 :type accept: function
582 :param maxdepth: maximum depth in the filesystem (25)
583 :type maxdepth: int
584 :param pats: list of patterns to accept and list of patterns to exclude
585 :type pats: tuple
586 :param dir: return folders too (False by default)
587 :type dir: bool
588 :param src: return files (True by default)
589 :type src: bool
590 :param remove: remove files/folders that do not exist (True by default)
591 :type remove: bool
592 :param quiet: disable build directory traversal warnings (verbose mode)
593 :type quiet: bool
594 :returns: A generator object to iterate from
595 :rtype: iterator
597 dircont = self.listdir()
598 dircont.sort()
600 try:
601 lst = set(self.children.keys())
602 except AttributeError:
603 self.children = self.dict_class()
604 else:
605 if remove:
606 for x in lst - set(dircont):
607 self.children[x].evict()
609 for name in dircont:
610 npats = accept(name, pats)
611 if npats and npats[0]:
612 accepted = [] in npats[0]
614 node = self.make_node([name])
616 isdir = node.isdir()
617 if accepted:
618 if isdir:
619 if dir:
620 yield node
621 elif src:
622 yield node
624 if isdir:
625 node.cache_isdir = True
626 if maxdepth:
627 for k in node.ant_iter(accept=accept, maxdepth=maxdepth - 1, pats=npats, dir=dir, src=src, remove=remove, quiet=quiet):
628 yield k
630 def ant_glob(self, *k, **kw):
632 Finds files across folders and returns Node objects:
634 * ``**/*`` find all files recursively
635 * ``**/*.class`` find all files ending by .class
636 * ``..`` find files having two dot characters
638 For example::
640 def configure(cfg):
641 # find all .cpp files
642 cfg.path.ant_glob('**/*.cpp')
643 # find particular files from the root filesystem (can be slow)
644 cfg.root.ant_glob('etc/*.txt')
645 # simple exclusion rule example
646 cfg.path.ant_glob('*.c*', excl=['*.c'], src=True, dir=False)
648 For more information about the patterns, consult http://ant.apache.org/manual/dirtasks.html
649 Please remember that the '..' sequence does not represent the parent directory::
651 def configure(cfg):
652 cfg.path.ant_glob('../*.h') # incorrect
653 cfg.path.parent.ant_glob('*.h') # correct
655 The Node structure is itself a filesystem cache, so certain precautions must
656 be taken while matching files in the build or installation phases.
657 Nodes objects that do have a corresponding file or folder are garbage-collected by default.
658 This garbage collection is usually required to prevent returning files that do not
659 exist anymore. Yet, this may also remove Node objects of files that are yet-to-be built.
661 This typically happens when trying to match files in the build directory,
662 but there are also cases when files are created in the source directory.
663 Run ``waf -v`` to display any warnings, and try consider passing ``remove=False``
664 when matching files in the build directory.
666 Since ant_glob can traverse both source and build folders, it is a best practice
667 to call this method only from the most specific build node::
669 def build(bld):
670 # traverses the build directory, may need ``remove=False``:
671 bld.path.ant_glob('project/dir/**/*.h')
672 # better, no accidental build directory traversal:
673 bld.path.find_node('project/dir').ant_glob('**/*.h') # best
675 In addition, files and folders are listed immediately. When matching files in the
676 build folders, consider passing ``generator=True`` so that the generator object
677 returned can defer computation to a later stage. For example::
679 def build(bld):
680 bld(rule='tar xvf ${SRC}', source='arch.tar')
681 bld.add_group()
682 gen = bld.bldnode.ant_glob("*.h", generator=True, remove=True)
683 # files will be listed only after the arch.tar is unpacked
684 bld(rule='ls ${SRC}', source=gen, name='XYZ')
687 :param incl: ant patterns or list of patterns to include
688 :type incl: string or list of strings
689 :param excl: ant patterns or list of patterns to exclude
690 :type excl: string or list of strings
691 :param dir: return folders too (False by default)
692 :type dir: bool
693 :param src: return files (True by default)
694 :type src: bool
695 :param maxdepth: maximum depth of recursion
696 :type maxdepth: int
697 :param ignorecase: ignore case while matching (False by default)
698 :type ignorecase: bool
699 :param generator: Whether to evaluate the Nodes lazily
700 :type generator: bool
701 :param remove: remove files/folders that do not exist (True by default)
702 :type remove: bool
703 :param quiet: disable build directory traversal warnings (verbose mode)
704 :type quiet: bool
705 :returns: The corresponding Node objects as a list or as a generator object (generator=True)
706 :rtype: by default, list of :py:class:`waflib.Node.Node` instances
708 src = kw.get('src', True)
709 dir = kw.get('dir')
710 excl = kw.get('excl', exclude_regs)
711 incl = k and k[0] or kw.get('incl', '**')
712 remove = kw.get('remove', True)
713 maxdepth = kw.get('maxdepth', 25)
714 ignorecase = kw.get('ignorecase', False)
715 quiet = kw.get('quiet', False)
716 pats = (ant_matcher(incl, ignorecase), ant_matcher(excl, ignorecase))
718 if kw.get('generator'):
719 return Utils.lazy_generator(self.ant_iter, (ant_sub_matcher, maxdepth, pats, dir, src, remove, quiet))
721 it = self.ant_iter(ant_sub_matcher, maxdepth, pats, dir, src, remove, quiet)
722 if kw.get('flat'):
723 # returns relative paths as a space-delimited string
724 # prefer Node objects whenever possible
725 return ' '.join(x.path_from(self) for x in it)
726 return list(it)
728 # ----------------------------------------------------------------------------
729 # the methods below require the source/build folders (bld.srcnode/bld.bldnode)
731 def is_src(self):
733 Returns True if the node is below the source directory. Note that ``!is_src() ≠ is_bld()``
735 :rtype: bool
737 cur = self
738 x = self.ctx.srcnode
739 y = self.ctx.bldnode
740 while cur.parent:
741 if cur is y:
742 return False
743 if cur is x:
744 return True
745 cur = cur.parent
746 return False
748 def is_bld(self):
750 Returns True if the node is below the build directory. Note that ``!is_bld() ≠ is_src()``
752 :rtype: bool
754 cur = self
755 y = self.ctx.bldnode
756 while cur.parent:
757 if cur is y:
758 return True
759 cur = cur.parent
760 return False
762 def get_src(self):
764 Returns the corresponding Node object in the source directory (or self if already
765 under the source directory). Use this method only if the purpose is to create
766 a Node object (this is common with folders but not with files, see ticket 1937)
768 :rtype: :py:class:`waflib.Node.Node`
770 cur = self
771 x = self.ctx.srcnode
772 y = self.ctx.bldnode
773 lst = []
774 while cur.parent:
775 if cur is y:
776 lst.reverse()
777 return x.make_node(lst)
778 if cur is x:
779 return self
780 lst.append(cur.name)
781 cur = cur.parent
782 return self
784 def get_bld(self):
786 Return the corresponding Node object in the build directory (or self if already
787 under the build directory). Use this method only if the purpose is to create
788 a Node object (this is common with folders but not with files, see ticket 1937)
790 :rtype: :py:class:`waflib.Node.Node`
792 cur = self
793 x = self.ctx.srcnode
794 y = self.ctx.bldnode
795 lst = []
796 while cur.parent:
797 if cur is y:
798 return self
799 if cur is x:
800 lst.reverse()
801 return self.ctx.bldnode.make_node(lst)
802 lst.append(cur.name)
803 cur = cur.parent
804 # the file is external to the current project, make a fake root in the current build directory
805 lst.reverse()
806 if lst and Utils.is_win32 and len(lst[0]) == 2 and lst[0].endswith(':'):
807 lst[0] = lst[0][0]
808 return self.ctx.bldnode.make_node(['__root__'] + lst)
810 def find_resource(self, lst):
812 Use this method in the build phase to find source files corresponding to the relative path given.
814 First it looks up the Node data structure to find any declared Node object in the build directory.
815 If None is found, it then considers the filesystem in the source directory.
817 :param lst: relative path
818 :type lst: string or list of string
819 :returns: the corresponding Node object or None
820 :rtype: :py:class:`waflib.Node.Node`
822 if isinstance(lst, str):
823 lst = [x for x in Utils.split_path(lst) if x and x != '.']
825 node = self.get_bld().search_node(lst)
826 if not node:
827 node = self.get_src().find_node(lst)
828 if node and node.isdir():
829 return None
830 return node
832 def find_or_declare(self, lst):
834 Use this method in the build phase to declare output files which
835 are meant to be written in the build directory.
837 This method creates the Node object and its parent folder
838 as needed.
840 :param lst: relative path
841 :type lst: string or list of string
843 if isinstance(lst, str) and os.path.isabs(lst):
844 node = self.ctx.root.make_node(lst)
845 else:
846 node = self.get_bld().make_node(lst)
847 node.parent.mkdir()
848 return node
850 def find_dir(self, lst):
852 Searches for a folder on the filesystem (see :py:meth:`waflib.Node.Node.find_node`)
854 :param lst: relative path
855 :type lst: string or list of string
856 :returns: The corresponding Node object or None if there is no such folder
857 :rtype: :py:class:`waflib.Node.Node`
859 if isinstance(lst, str):
860 lst = [x for x in Utils.split_path(lst) if x and x != '.']
862 node = self.find_node(lst)
863 if node and not node.isdir():
864 return None
865 return node
867 # helpers for building things
868 def change_ext(self, ext, ext_in=None):
870 Declares a build node with a distinct extension; this is uses :py:meth:`waflib.Node.Node.find_or_declare`
872 :return: A build node of the same path, but with a different extension
873 :rtype: :py:class:`waflib.Node.Node`
875 name = self.name
876 if ext_in is None:
877 k = name.rfind('.')
878 if k >= 0:
879 name = name[:k] + ext
880 else:
881 name = name + ext
882 else:
883 name = name[:- len(ext_in)] + ext
885 return self.parent.find_or_declare([name])
887 def bldpath(self):
889 Returns the relative path seen from the build directory ``src/foo.cpp``
891 :rtype: string
893 return self.path_from(self.ctx.bldnode)
895 def srcpath(self):
897 Returns the relative path seen from the source directory ``../src/foo.cpp``
899 :rtype: string
901 return self.path_from(self.ctx.srcnode)
903 def relpath(self):
905 If a file in the build directory, returns :py:meth:`waflib.Node.Node.bldpath`,
906 else returns :py:meth:`waflib.Node.Node.srcpath`
908 :rtype: string
910 cur = self
911 x = self.ctx.bldnode
912 while cur.parent:
913 if cur is x:
914 return self.bldpath()
915 cur = cur.parent
916 return self.srcpath()
918 def bld_dir(self):
920 Equivalent to self.parent.bldpath()
922 :rtype: string
924 return self.parent.bldpath()
926 def h_file(self):
928 See :py:func:`waflib.Utils.h_file`
930 :return: a hash representing the file contents
931 :rtype: string or bytes
933 return Utils.h_file(self.abspath())
935 def get_bld_sig(self):
937 Returns a signature (see :py:meth:`waflib.Node.Node.h_file`) for the purpose
938 of build dependency calculation. This method uses a per-context cache.
940 :return: a hash representing the object contents
941 :rtype: string or bytes
943 # previous behaviour can be set by returning self.ctx.node_sigs[self] when a build node
944 try:
945 cache = self.ctx.cache_sig
946 except AttributeError:
947 cache = self.ctx.cache_sig = {}
948 try:
949 ret = cache[self]
950 except KeyError:
951 p = self.abspath()
952 try:
953 ret = cache[self] = self.h_file()
954 except EnvironmentError:
955 if self.isdir():
956 # allow folders as build nodes, do not use the creation time
957 st = os.stat(p)
958 ret = cache[self] = Utils.h_list([p, st.st_ino, st.st_mode])
959 return ret
960 raise
961 return ret
963 pickle_lock = Utils.threading.Lock()
964 """Lock mandatory for thread-safe node serialization"""
966 class Nod3(Node):
967 """Mandatory subclass for thread-safe node serialization"""
968 pass # do not remove