waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

Node.py (25493B)


      1 #!/usr/bin/env python
      2 # encoding: utf-8
      3 # Thomas Nagy, 2005-2018 (ita)
      4 
      5 """
      6 Node: filesystem structure
      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` for the low-level operations
     15 
     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
     18 
     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 """
     23 
     24 import os, re, sys, shutil
     25 from waflib import Utils, Errors
     26 
     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 """
     63 
     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
     86 
     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
    102 
    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]
    109 
    110 class Node(object):
    111 	"""
    112 	This class is organized in two parts:
    113 
    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``)
    116 	"""
    117 
    118 	dict_class = dict
    119 	"""
    120 	Subclasses can provide a dict class to enable case insensitivity for example.
    121 	"""
    122 
    123 	__slots__ = ('name', 'parent', 'children', 'cache_abspath', 'cache_isdir')
    124 	def __init__(self, name, parent):
    125 		"""
    126 		.. note:: Use :py:func:`Node.make_node` or :py:func:`Node.find_node` instead of calling this constructor
    127 		"""
    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
    134 
    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])
    142 
    143 	def __getstate__(self):
    144 		"Serializes node information, used for persistence"
    145 		return (self.name, self.parent, getattr(self, 'children', None))
    146 
    147 	def __str__(self):
    148 		"""
    149 		String representation (abspath), for debugging purposes
    150 
    151 		:rtype: string
    152 		"""
    153 		return self.abspath()
    154 
    155 	def __repr__(self):
    156 		"""
    157 		String representation (abspath), for debugging purposes
    158 
    159 		:rtype: string
    160 		"""
    161 		return self.abspath()
    162 
    163 	def __copy__(self):
    164 		"""
    165 		Provided to prevent nodes from being copied
    166 
    167 		:raises: :py:class:`waflib.Errors.WafError`
    168 		"""
    169 		raise Errors.WafError('nodes are not supposed to be copied')
    170 
    171 	def read(self, flags='r', encoding='latin-1'):
    172 		"""
    173 		Reads and returns the contents of the file represented by this node, see :py:func:`waflib.Utils.readf`::
    174 
    175 			def build(bld):
    176 				bld.path.find_node('wscript').read()
    177 
    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
    184 		"""
    185 		return Utils.readf(self.abspath(), flags, encoding)
    186 
    187 	def write(self, data, flags='w', encoding='latin-1'):
    188 		"""
    189 		Writes data to the file represented by this node, see :py:func:`waflib.Utils.writef`::
    190 
    191 			def build(bld):
    192 				bld.path.make_node('foo.txt').write('Hello, world!')
    193 
    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
    200 		"""
    201 		Utils.writef(self.abspath(), data, flags, encoding)
    202 
    203 	def read_json(self, convert=True, encoding='utf-8'):
    204 		"""
    205 		Reads and parses the contents of this node as JSON (Python ≥ 2.6)::
    206 
    207 			def build(bld):
    208 				bld.path.find_node('abc.json').read_json()
    209 
    210 		Note that this by default automatically decodes unicode strings on Python2, unlike what the Python JSON module does.
    211 
    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
    218 		"""
    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
    226 
    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
    234 
    235 			def object_pairs(pairs):
    236 				return dict((str(pair[0]), convert(pair[1])) for pair in pairs)
    237 
    238 			object_pairs_hook = object_pairs
    239 
    240 		return json.loads(self.read(encoding=encoding), object_pairs_hook=object_pairs_hook)
    241 
    242 	def write_json(self, data, pretty=True):
    243 		"""
    244 		Writes a python object as JSON to disk (Python ≥ 2.6) as UTF-8 data (JSON standard)::
    245 
    246 			def build(bld):
    247 				bld.path.find_node('xyz.json').write_json(199)
    248 
    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
    253 		"""
    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')
    265 
    266 	def exists(self):
    267 		"""
    268 		Returns whether the Node is present on the filesystem
    269 
    270 		:rtype: bool
    271 		"""
    272 		return os.path.exists(self.abspath())
    273 
    274 	def isdir(self):
    275 		"""
    276 		Returns whether the Node represents a folder
    277 
    278 		:rtype: bool
    279 		"""
    280 		return os.path.isdir(self.abspath())
    281 
    282 	def chmod(self, val):
    283 		"""
    284 		Changes the file/dir permissions::
    285 
    286 			def build(bld):
    287 				bld.path.chmod(493) # 0755
    288 		"""
    289 		os.chmod(self.abspath(), val)
    290 
    291 	def delete(self, evict=True):
    292 		"""
    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.
    295 		"""
    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()
    308 
    309 	def evict(self):
    310 		"""
    311 		Removes this node from the Node tree
    312 		"""
    313 		del self.parent.children[self.name]
    314 
    315 	def suffix(self):
    316 		"""
    317 		Returns the file rightmost extension, for example `a.b.c.d → .d`
    318 
    319 		:rtype: string
    320 		"""
    321 		k = max(0, self.name.rfind('.'))
    322 		return self.name[k:]
    323 
    324 	def height(self):
    325 		"""
    326 		Returns the depth in the folder hierarchy from the filesystem root or from all the file drives
    327 
    328 		:returns: filesystem depth
    329 		:rtype: integer
    330 		"""
    331 		d = self
    332 		val = -1
    333 		while d:
    334 			d = d.parent
    335 			val += 1
    336 		return val
    337 
    338 	def listdir(self):
    339 		"""
    340 		Lists the folder contents
    341 
    342 		:returns: list of file/folder names ordered alphabetically
    343 		:rtype: list of string
    344 		"""
    345 		lst = Utils.listdir(self.abspath())
    346 		lst.sort()
    347 		return lst
    348 
    349 	def mkdir(self):
    350 		"""
    351 		Creates a folder represented by this node. Intermediate folders are created as needed.
    352 
    353 		:raises: :py:class:`waflib.Errors.WafError` when the folder is missing
    354 		"""
    355 		if self.isdir():
    356 			return
    357 
    358 		try:
    359 			self.parent.mkdir()
    360 		except OSError:
    361 			pass
    362 
    363 		if self.name:
    364 			try:
    365 				os.makedirs(self.abspath())
    366 			except OSError:
    367 				pass
    368 
    369 			if not self.isdir():
    370 				raise Errors.WafError('Could not create the directory %r' % self)
    371 
    372 			try:
    373 				self.children
    374 			except AttributeError:
    375 				self.children = self.dict_class()
    376 
    377 	def find_node(self, lst):
    378 		"""
    379 		Finds a node on the file system (files or folders), and creates the corresponding Node objects if it exists
    380 
    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´
    385 		"""
    386 
    387 		if isinstance(lst, str):
    388 			lst = [x for x in Utils.split_path(lst) if x and x != '.']
    389 
    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:])
    394 
    395 		cur = self
    396 		for x in lst:
    397 			if x == '..':
    398 				cur = cur.parent or cur
    399 				continue
    400 
    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
    411 
    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
    417 
    418 		if not cur.exists():
    419 			cur.evict()
    420 			return None
    421 
    422 		return cur
    423 
    424 	def make_node(self, lst):
    425 		"""
    426 		Returns or creates a Node object corresponding to the input path without considering the filesystem.
    427 
    428 		:param lst: relative path
    429 		:type lst: string or list of string
    430 		:rtype: :py:class:´waflib.Node.Node´
    431 		"""
    432 		if isinstance(lst, str):
    433 			lst = [x for x in Utils.split_path(lst) if x and x != '.']
    434 
    435 		cur = self
    436 		for x in lst:
    437 			if x == '..':
    438 				cur = cur.parent or cur
    439 				continue
    440 
    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
    451 
    452 	def search_node(self, lst):
    453 		"""
    454 		Returns a Node previously defined in the data structure. The filesystem is not considered.
    455 
    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
    459 		"""
    460 		if isinstance(lst, str):
    461 			lst = [x for x in Utils.split_path(lst) if x and x != '.']
    462 
    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
    473 
    474 	def path_from(self, node):
    475 		"""
    476 		Path of this node seen from the other::
    477 
    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'
    482 
    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
    487 		"""
    488 		c1 = self
    489 		c2 = node
    490 
    491 		c1h = c1.height()
    492 		c2h = c2.height()
    493 
    494 		lst = []
    495 		up = 0
    496 
    497 		while c1h > c2h:
    498 			lst.append(c1.name)
    499 			c1 = c1.parent
    500 			c1h -= 1
    501 
    502 		while c2h > c1h:
    503 			up += 1
    504 			c2 = c2.parent
    505 			c2h -= 1
    506 
    507 		while not c1 is c2:
    508 			lst.append(c1.name)
    509 			up += 1
    510 
    511 			c1 = c1.parent
    512 			c2 = c2.parent
    513 
    514 		if c1.parent:
    515 			lst.extend(['..'] * up)
    516 			lst.reverse()
    517 			return os.sep.join(lst) or '.'
    518 		else:
    519 			return self.abspath()
    520 
    521 	def abspath(self):
    522 		"""
    523 		Returns the absolute path. A cache is kept in the context as ``cache_node_abspath``
    524 
    525 		:rtype: string
    526 		"""
    527 		try:
    528 			return self.cache_abspath
    529 		except AttributeError:
    530 			pass
    531 		# think twice before touching this (performance + complexity + correctness)
    532 
    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
    541 
    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
    556 
    557 	def is_child_of(self, node):
    558 		"""
    559 		Returns whether the object belongs to a subtree of the input node::
    560 
    561 			def build(bld):
    562 				node = bld.path.find_node('wscript')
    563 				node.is_child_of(bld.path) # True
    564 
    565 		:param node: path to use as a reference
    566 		:type node: :py:class:`waflib.Node.Node`
    567 		:rtype: bool
    568 		"""
    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
    575 
    576 	def ant_iter(self, accept=None, maxdepth=25, pats=[], dir=False, src=True, remove=True, quiet=False):
    577 		"""
    578 		Recursive method used by :py:meth:`waflib.Node.ant_glob`.
    579 
    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
    596 		"""
    597 		dircont = self.listdir()
    598 
    599 		try:
    600 			lst = set(self.children.keys())
    601 		except AttributeError:
    602 			self.children = self.dict_class()
    603 		else:
    604 			if remove:
    605 				for x in lst - set(dircont):
    606 					self.children[x].evict()
    607 
    608 		for name in dircont:
    609 			npats = accept(name, pats)
    610 			if npats and npats[0]:
    611 				accepted = [] in npats[0]
    612 
    613 				node = self.make_node([name])
    614 
    615 				isdir = node.isdir()
    616 				if accepted:
    617 					if isdir:
    618 						if dir:
    619 							yield node
    620 					elif src:
    621 						yield node
    622 
    623 				if isdir:
    624 					node.cache_isdir = True
    625 					if maxdepth:
    626 						for k in node.ant_iter(accept=accept, maxdepth=maxdepth - 1, pats=npats, dir=dir, src=src, remove=remove, quiet=quiet):
    627 							yield k
    628 
    629 	def ant_glob(self, *k, **kw):
    630 		"""
    631 		Finds files across folders and returns Node objects:
    632 
    633 		* ``**/*`` find all files recursively
    634 		* ``**/*.class`` find all files ending by .class
    635 		* ``..`` find files having two dot characters
    636 
    637 		For example::
    638 
    639 			def configure(cfg):
    640 				# find all .cpp files
    641 				cfg.path.ant_glob('**/*.cpp')
    642 				# find particular files from the root filesystem (can be slow)
    643 				cfg.root.ant_glob('etc/*.txt')
    644 				# simple exclusion rule example
    645 				cfg.path.ant_glob('*.c*', excl=['*.c'], src=True, dir=False)
    646 
    647 		For more information about the patterns, consult http://ant.apache.org/manual/dirtasks.html
    648 		Please remember that the '..' sequence does not represent the parent directory::
    649 
    650 			def configure(cfg):
    651 				cfg.path.ant_glob('../*.h') # incorrect
    652 				cfg.path.parent.ant_glob('*.h') # correct
    653 
    654 		The Node structure is itself a filesystem cache, so certain precautions must
    655 		be taken while matching files in the build or installation phases.
    656 		Nodes objects that do have a corresponding file or folder are garbage-collected by default.
    657 		This garbage collection is usually required to prevent returning files that do not
    658 		exist anymore. Yet, this may also remove Node objects of files that are yet-to-be built.
    659 
    660 		This typically happens when trying to match files in the build directory,
    661 		but there are also cases when files are created in the source directory.
    662 		Run ``waf -v`` to display any warnings, and try consider passing ``remove=False``
    663 		when matching files in the build directory.
    664 
    665 		Since ant_glob can traverse both source and build folders, it is a best practice
    666 		to call this method only from the most specific build node::
    667 
    668 			def build(bld):
    669 				# traverses the build directory, may need ``remove=False``:
    670 				bld.path.ant_glob('project/dir/**/*.h')
    671 				# better, no accidental build directory traversal:
    672 				bld.path.find_node('project/dir').ant_glob('**/*.h') # best
    673 
    674 		In addition, files and folders are listed immediately. When matching files in the
    675 		build folders, consider passing ``generator=True`` so that the generator object
    676 		returned can defer computation to a later stage. For example::
    677 
    678 			def build(bld):
    679 				bld(rule='tar xvf ${SRC}', source='arch.tar')
    680 				bld.add_group()
    681 				gen = bld.bldnode.ant_glob("*.h", generator=True, remove=True)
    682 				# files will be listed only after the arch.tar is unpacked
    683 				bld(rule='ls ${SRC}', source=gen, name='XYZ')
    684 
    685 
    686 		:param incl: ant patterns or list of patterns to include
    687 		:type incl: string or list of strings
    688 		:param excl: ant patterns or list of patterns to exclude
    689 		:type excl: string or list of strings
    690 		:param dir: return folders too (False by default)
    691 		:type dir: bool
    692 		:param src: return files (True by default)
    693 		:type src: bool
    694 		:param maxdepth: maximum depth of recursion
    695 		:type maxdepth: int
    696 		:param ignorecase: ignore case while matching (False by default)
    697 		:type ignorecase: bool
    698 		:param generator: Whether to evaluate the Nodes lazily
    699 		:type generator: bool
    700 		:param remove: remove files/folders that do not exist (True by default)
    701 		:type remove: bool
    702 		:param quiet: disable build directory traversal warnings (verbose mode)
    703 		:type quiet: bool
    704 		:returns: The corresponding Node objects as a list or as a generator object (generator=True)
    705 		:rtype: by default, list of :py:class:`waflib.Node.Node` instances
    706 		"""
    707 		src = kw.get('src', True)
    708 		dir = kw.get('dir')
    709 		excl = kw.get('excl', exclude_regs)
    710 		incl = k and k[0] or kw.get('incl', '**')
    711 		remove = kw.get('remove', True)
    712 		maxdepth = kw.get('maxdepth', 25)
    713 		ignorecase = kw.get('ignorecase', False)
    714 		quiet = kw.get('quiet', False)
    715 		pats = (ant_matcher(incl, ignorecase), ant_matcher(excl, ignorecase))
    716 
    717 		if kw.get('generator'):
    718 			return Utils.lazy_generator(self.ant_iter, (ant_sub_matcher, maxdepth, pats, dir, src, remove, quiet))
    719 
    720 		it = self.ant_iter(ant_sub_matcher, maxdepth, pats, dir, src, remove, quiet)
    721 		if kw.get('flat'):
    722 			# returns relative paths as a space-delimited string
    723 			# prefer Node objects whenever possible
    724 			return ' '.join(x.path_from(self) for x in it)
    725 		return list(it)
    726 
    727 	# ----------------------------------------------------------------------------
    728 	# the methods below require the source/build folders (bld.srcnode/bld.bldnode)
    729 
    730 	def is_src(self):
    731 		"""
    732 		Returns True if the node is below the source directory. Note that ``!is_src() ≠ is_bld()``
    733 
    734 		:rtype: bool
    735 		"""
    736 		cur = self
    737 		x = self.ctx.srcnode
    738 		y = self.ctx.bldnode
    739 		while cur.parent:
    740 			if cur is y:
    741 				return False
    742 			if cur is x:
    743 				return True
    744 			cur = cur.parent
    745 		return False
    746 
    747 	def is_bld(self):
    748 		"""
    749 		Returns True if the node is below the build directory. Note that ``!is_bld() ≠ is_src()``
    750 
    751 		:rtype: bool
    752 		"""
    753 		cur = self
    754 		y = self.ctx.bldnode
    755 		while cur.parent:
    756 			if cur is y:
    757 				return True
    758 			cur = cur.parent
    759 		return False
    760 
    761 	def get_src(self):
    762 		"""
    763 		Returns the corresponding Node object in the source directory (or self if already
    764 		under the source directory). Use this method only if the purpose is to create
    765 		a Node object (this is common with folders but not with files, see ticket 1937)
    766 
    767 		:rtype: :py:class:`waflib.Node.Node`
    768 		"""
    769 		cur = self
    770 		x = self.ctx.srcnode
    771 		y = self.ctx.bldnode
    772 		lst = []
    773 		while cur.parent:
    774 			if cur is y:
    775 				lst.reverse()
    776 				return x.make_node(lst)
    777 			if cur is x:
    778 				return self
    779 			lst.append(cur.name)
    780 			cur = cur.parent
    781 		return self
    782 
    783 	def get_bld(self):
    784 		"""
    785 		Return the corresponding Node object in the build directory (or self if already
    786 		under the build directory). Use this method only if the purpose is to create
    787 		a Node object (this is common with folders but not with files, see ticket 1937)
    788 
    789 		:rtype: :py:class:`waflib.Node.Node`
    790 		"""
    791 		cur = self
    792 		x = self.ctx.srcnode
    793 		y = self.ctx.bldnode
    794 		lst = []
    795 		while cur.parent:
    796 			if cur is y:
    797 				return self
    798 			if cur is x:
    799 				lst.reverse()
    800 				return self.ctx.bldnode.make_node(lst)
    801 			lst.append(cur.name)
    802 			cur = cur.parent
    803 		# the file is external to the current project, make a fake root in the current build directory
    804 		lst.reverse()
    805 		if lst and Utils.is_win32 and len(lst[0]) == 2 and lst[0].endswith(':'):
    806 			lst[0] = lst[0][0]
    807 		return self.ctx.bldnode.make_node(['__root__'] + lst)
    808 
    809 	def find_resource(self, lst):
    810 		"""
    811 		Use this method in the build phase to find source files corresponding to the relative path given.
    812 
    813 		First it looks up the Node data structure to find any declared Node object in the build directory.
    814 		If None is found, it then considers the filesystem in the source directory.
    815 
    816 		:param lst: relative path
    817 		:type lst: string or list of string
    818 		:returns: the corresponding Node object or None
    819 		:rtype: :py:class:`waflib.Node.Node`
    820 		"""
    821 		if isinstance(lst, str):
    822 			lst = [x for x in Utils.split_path(lst) if x and x != '.']
    823 
    824 		node = self.get_bld().search_node(lst)
    825 		if not node:
    826 			node = self.get_src().find_node(lst)
    827 		if node and node.isdir():
    828 			return None
    829 		return node
    830 
    831 	def find_or_declare(self, lst):
    832 		"""
    833 		Use this method in the build phase to declare output files which
    834 		are meant to be written in the build directory.
    835 
    836 		This method creates the Node object and its parent folder
    837 		as needed.
    838 
    839 		:param lst: relative path
    840 		:type lst: string or list of string
    841 		"""
    842 		if isinstance(lst, str) and os.path.isabs(lst):
    843 			node = self.ctx.root.make_node(lst)
    844 		else:
    845 			node = self.get_bld().make_node(lst)
    846 		node.parent.mkdir()
    847 		return node
    848 
    849 	def find_dir(self, lst):
    850 		"""
    851 		Searches for a folder on the filesystem (see :py:meth:`waflib.Node.Node.find_node`)
    852 
    853 		:param lst: relative path
    854 		:type lst: string or list of string
    855 		:returns: The corresponding Node object or None if there is no such folder
    856 		:rtype: :py:class:`waflib.Node.Node`
    857 		"""
    858 		if isinstance(lst, str):
    859 			lst = [x for x in Utils.split_path(lst) if x and x != '.']
    860 
    861 		node = self.find_node(lst)
    862 		if node and not node.isdir():
    863 			return None
    864 		return node
    865 
    866 	# helpers for building things
    867 	def change_ext(self, ext, ext_in=None):
    868 		"""
    869 		Declares a build node with a distinct extension; this is uses :py:meth:`waflib.Node.Node.find_or_declare`
    870 
    871 		:return: A build node of the same path, but with a different extension
    872 		:rtype: :py:class:`waflib.Node.Node`
    873 		"""
    874 		name = self.name
    875 		if ext_in is None:
    876 			k = name.rfind('.')
    877 			if k >= 0:
    878 				name = name[:k] + ext
    879 			else:
    880 				name = name + ext
    881 		else:
    882 			name = name[:- len(ext_in)] + ext
    883 
    884 		return self.parent.find_or_declare([name])
    885 
    886 	def bldpath(self):
    887 		"""
    888 		Returns the relative path seen from the build directory ``src/foo.cpp``
    889 
    890 		:rtype: string
    891 		"""
    892 		return self.path_from(self.ctx.bldnode)
    893 
    894 	def srcpath(self):
    895 		"""
    896 		Returns the relative path seen from the source directory ``../src/foo.cpp``
    897 
    898 		:rtype: string
    899 		"""
    900 		return self.path_from(self.ctx.srcnode)
    901 
    902 	def relpath(self):
    903 		"""
    904 		If a file in the build directory, returns :py:meth:`waflib.Node.Node.bldpath`,
    905 		else returns :py:meth:`waflib.Node.Node.srcpath`
    906 
    907 		:rtype: string
    908 		"""
    909 		cur = self
    910 		x = self.ctx.bldnode
    911 		while cur.parent:
    912 			if cur is x:
    913 				return self.bldpath()
    914 			cur = cur.parent
    915 		return self.srcpath()
    916 
    917 	def bld_dir(self):
    918 		"""
    919 		Equivalent to self.parent.bldpath()
    920 
    921 		:rtype: string
    922 		"""
    923 		return self.parent.bldpath()
    924 
    925 	def h_file(self):
    926 		"""
    927 		See :py:func:`waflib.Utils.h_file`
    928 
    929 		:return: a hash representing the file contents
    930 		:rtype: string or bytes
    931 		"""
    932 		return Utils.h_file(self.abspath())
    933 
    934 	def get_bld_sig(self):
    935 		"""
    936 		Returns a signature (see :py:meth:`waflib.Node.Node.h_file`) for the purpose
    937 		of build dependency calculation. This method uses a per-context cache.
    938 
    939 		:return: a hash representing the object contents
    940 		:rtype: string or bytes
    941 		"""
    942 		# previous behaviour can be set by returning self.ctx.node_sigs[self] when a build node
    943 		try:
    944 			cache = self.ctx.cache_sig
    945 		except AttributeError:
    946 			cache = self.ctx.cache_sig = {}
    947 		try:
    948 			ret = cache[self]
    949 		except KeyError:
    950 			p = self.abspath()
    951 			try:
    952 				ret = cache[self] = self.h_file()
    953 			except EnvironmentError:
    954 				if self.isdir():
    955 					# allow folders as build nodes, do not use the creation time
    956 					st = os.stat(p)
    957 					ret = cache[self] = Utils.h_list([p, st.st_ino, st.st_mode])
    958 					return ret
    959 				raise
    960 		return ret
    961 
    962 pickle_lock = Utils.threading.Lock()
    963 """Lock mandatory for thread-safe node serialization"""
    964 
    965 class Nod3(Node):
    966 	"""Mandatory subclass for thread-safe node serialization"""
    967 	pass # do not remove
    968 
    969