Node objects represent files or folders and are used to ease the operations dealing with the file system. This chapter provides an overview of their usage.
=== Design of the node class
==== The node tree
The Waf nodes inherit the class _waflib.Node.Node_ and provide a tree structure to represent the file system:
. *parent*: parent node
. *children*: folder contents - or empty if the node is a file
In practice, the reference to the filesystem tree is bound to the context classes for access from Waf commands. Here is an illustration:
<2> The folder contents are stored in the dict _children_ which maps names to node objects
<3> Each node keeps reference to his _parent_ node
<4> The root node has no _parent_
NOTE: There is a strict correspondance between nodes and filesystem elements: a node represents exactly one file or one folder, and only one node can represent a file or a folder.
==== Node caching
By default, only the necessary nodes are created:
// nodes_cache
[source,python]
---------------
def configure(ctx):
pass
def dosomething(ctx):
print(ctx.root.children)
---------------
The filesystem root appears to only contain one node, although the real filesystem root contains more folders than just +/tmp+:
[source,shishell]
---------------
$ waf configure dosomething
Setting top to : /tmp/nodes_cache
Setting out to : /tmp/nodes_cache/build
'configure' finished successfully (0.086s)
{'tmp': /tmp}
'dosomething' finished successfully (0.001s)
$ ls /
bin boot dev etc home tmp usr var
---------------
This means in particular that some nodes may have to be read from the file system or created before being used.
// ==== nodes and signatures TODO
=== General usage
==== Searching and creating nodes
Nodes may be created manually or read from the file system. Three methods are provided for this purpose:
WARNING: The Node methods are not meant to be safe for concurrent access. The code executed in parallel (method run() of task objects for example) must avoid modifying the Node object data structure.
WARNING: The Node methods read/write must be used to prevent file handle inheritance issues on win32 systems instead of plain open/read/write. Such problems arise when spawning processes during parallel builds.
<1> The method ant_glob is called on a node object, and not on the build context, it returns only files by default
<2> Patterns may contain wildcards such as '*' or '?', but they are http://ant.apache.org/manual/dirtasks.html[Ant patterns], not regular expressions
<3> The symbol '**' enable recursion. Complex folder hierarchies may take a lot of time, so use with care.
<4> Even though recursion is enabled, only files are returned by default. To turn directory listing on, use 'dir=True'
<5> Patterns are either lists of strings or space-delimited values. Patterns to exclude are defined in 'waflib.Node.exclude_regs'.
The execution output will be the following:
[source,shishell]
---------------
$ waf configure dosomething
Setting top to : /tmp/nodes_ant_glob
Setting out to : /tmp/nodes_ant_glob/build
'configure' finished successfully (0.006s)
[/tmp/nodes_ant_glob/wscript]
[/tmp/nodes_ant_glob/wscript]
[/usr/include/zlib.h]
[/tmp/nodes_ant_glob/build/c4che/build.config.py]
---------------
The sequence '..' represents exactly two dot characters, and not the parent directory. This is used to guarantee that the search will terminate, and that the same files will not be listed multiple times. Consider the following:
[source,python]
---------------
ctx.path.ant_glob('../wscript') <1>
ctx.path.parent.ant_glob('wscript') <2>
---------------
<1> Invalid, this pattern will never return anything
Although the _sources_ and _targets_ in the +wscript+ files are declared as if they were in the current directory, the target files are output into the build directory. To enable this behaviour, the directory structure below the _top_ directory must be replicated in the _out_ directory. For example, the folder *program* from +demos/c+ has its equivalent in the build directory:
[source,shishell]
---------------
$ cd demos/c
$ tree
.
|-- build
| |-- c4che
| | |-- build.config.py
| | `-- _cache.py
| |-- config.h
| |-- config.log
| `-- program
| |-- main.c.0.o
| `-- myprogram
|-- program
| |-- a.h
| |-- main.c
| `-- wscript_build
`-- wscript
---------------
To support this, the build context provides two additional nodes:
. srcnode: node representing the top-level directory
. bldnode: node representing the build directory
To obtain a build node from a src node and vice-versa, the following methods may be used:
. Node.get_src()
. Node.get_bld()
==== Using Nodes during the build phase
Although using _srcnode_ and _bldnode_ directly is possible, the three following wrapper methods are much easier to use. They accept a string representing the target as input and return a single node:
. *find_dir*: returns a node or None if the folder cannot be found on the system.
. *find_resource*: returns a node under the source directory, a node under the corresponding build directory, or None if no such a node exists. If the file is not in the build directory, the node signature is computed and put into a cache (file contents hash).
. *find_or_declare*: returns a node or create the corresponding node in the build directory.
Besides, they all use _find_dir_ internally which will create the required directory structure in the build directory. Because the folders may be replicated in the build directory before the build starts, it is recommended to use it whenever possible:
[source,python]
---------------
def build(bld):
p = bld.path.parent.find_dir('src') <1>
p = bld.path.find_dir('../src') <2>
---------------
<1> Not recommended, use _find_dir_ instead
<2> Path separators are converted automatically according to the platform.
==== Nodes, tasks, and task generators
As seen in the previous chapter, Task objects can process files represented as lists of input and output nodes. The task generators
will usually process the input files given as strings to obtain such nodes and bind them to the tasks.
Because the build directory can be enabled or disabled, the following file copy would be ambiguous: footnote:[When file copies cannot be avoided, the best practice is to change the file names]