== The scripting system

This chapter provides a practical overview of the waf usage in a software project.

=== Setting up a project

The Waf projects are structured based on the following concepts:

. Source directory: directory containing the source files that will be packaged and redistributed to other developers or to end users
. Build directory: directory containing the files generated by the project (configuration sets, build files, logs, etc)
. System files: files and folders which do not belong to the project (operating system files, etc)

When Waf is launched, it looks for the top-level project file, which are Python scripts. A valid Waf project requires at least a top-level Waf script file (named 'wscript' without any extension) containing the three following elements:

. top: string representing the project directory. In general, top is set to \'.\', except for some proprietary projects where the wscript cannot be added to the top-level, top may be set to \'../..' or even some other folder such as '/checkout/perforce/project'
. out: string representing the build directory. In general, it is set to 'build', except for some proprietary projects where the build directory may be set to an absolute path such as '/tmp/build'. It is important to be able to remove the build directory safely, so it should never be given as \'.' or \'..'.
. configure: function called for setting up a project (also known as a 'Waf command').

The Waf script will always look for one valid top-level project file first before considering other project files.

Now let us create a new Waf project in the folder '/tmp/smallproject'. The first step is to write a wscript file in '/tmp/smallproject/wscript' with the following contents:

[source,python]
---------------
top = '.'
out = 'build_directory'

def configure(ctx):
	print('→ configuring the project')
---------------

To use a Waf project for the first time, it is necessary to initialize it. Waf will then validate the project file, and create the cache files for later use (lock file, build directory, store the project options):

[source,shishell]
---------------
$ cd /tmp/smallproject <1>
$ tree
.
`-- wscript

$ waf configure <2>
→ configuring the project
'configure' finished successfully (0.021s)

$ tree
.
|-- build_directory/ <3>
|   |-- c4che/ <4>
|   |   |-- build.config.py <5>
|   |   `-- default.cache.py <6>
|   |-- config.log <7>
|   `-- default/ <8>
|--.lock-wscript <9>
`-- wscript
---------------

<1> To configure the project, go to the folder containing the top-level project file
<2> The execution is called by calling `waf configure`
<3> The build directory was created
<4> The configuration data is stored in the folder 'c4che/'
<5> The command-line options and environment variables in use are stored in this file
<6> The user configuration set is stored in this file
<7> Configuration log (duplicate of the output generated during the configuration)
<8> Directory for the generated files (none so far)
<9> Lock file pointing at the relevant project file and build directory

=== Adding project commands

In the last section, we have see the use of the command 'configure' to initialize a project. Waf commands are special functions declared in the top-level project file and which may be called explicitely by `waf commandname`. Let us create two new commands _print_ping_ and _print_pong_ in the project created previously ('/tmp/smallproject/')

[source,python]
---------------
top = '.'
out = 'build_directory'

def configure(ctx):
	print('→ configuring the project')

def print_ping(ctx):
	print(' ping!')

def print_pong(ctx):
	print(' pong!')
---------------

Waf commands always take a single parameter called the _context_, which is used to ease data sharing between the scripts. To execute the new commands:

[source,shishell]
---------------
$ cd /tmp/smallproject

$ waf configure
→ configuring the project
'configure' finished successfully (0.001s)

$ waf print_ping
 ping!
'print_ping' finished successfully (0.000s)

$ waf print_pong
 pong!
'print_pong' finished successfully (0.000s)
---------------

Waf commands may also be executed at once by chaining them:

[source,shishell]
---------------
$ waf print_ping print_pong print_ping
 ping!
'print_ping' finished successfully (0.000s)
 pong!
'print_pong' finished successfully (0.000s)
 ping!
'print_ping' finished successfully (0.000s)
---------------

Waf commands may be repeated several times too:

[source,shishell]
---------------
$ waf print_ping print_ping print_ping
 ping!
'print_ping' finished successfully (0.000s)
 pong!
'print_ping' finished successfully (0.000s)
 ping!
'print_ping' finished successfully (0.000s)
---------------

=== Cleaning up a project

Waf itself comes with a predefined command called _distclean_ which removes the build directory and the lock file. After calling cleaning a project, it is necessary to configure it once again.

[source,shishell]
---------------
$ waf configure
→ configuring the project
'configure' finished successfully (0.001s)

$ waf print_ping
 ping!
'print_ping' finished successfully (0.000s)

$ waf distclean
'distclean' finished successfully (0.001s)

$ waf print_ping
Project not configured (run 'waf configure' first)
---------------

It is possible to override the behaviour of 'distclean' by redefining it in the wscript file. For example, the following will cause it to avoid removing the build files.

[source,python]
---------------
top = '.'
out = 'build_directory'

def configure(ctx):
	print('→ configuring the project')

def distclean(ctx):
	print(' Not cleaning anything!')
---------------

Upon execution:

[source,shishell]
---------------
$ waf distclean
 not cleaning anything!
'distclean' finished successfully (0.000s)
---------------

=== Packaging the project sources

The command 'dist' is another predefined utility which is used to create an archive of the project. By using the script presented previously:

[source,python]
---------------
top = '.'
out = 'build_directory'

def configure(ctx):
	print('→ configuring the project')
---------------

Execute the command 'dist' to get:

[source,shishell]
---------------
$ waf configure
→ configuring the project
'configure' finished successfully (0.001s)

$ waf dist
New archive created: noname-1.0.tar.bz2 (sha='c16c97a51b39c7e5bee35bb6d932a12e2952f2f8')
'dist' finished successfully (0.091s)
---------------

By default, the project name and version are set to 'noname' and '1.0'. To change them, it is necessary to provide two additional variables in the top-level project file:

[source,python]
---------------
APPNAME='webe'
VERSION='2.0'

top = '.'
out = 'build_directory'

def configure(ctx):
	print('→ configuring the project')
---------------

Because the project was configured once, it is not necessary to configure it once again:

[source,shishell]
---------------
$ waf dist
New archive created: webe-2.0.tar.bz2 (sha='7ccc338e2ff99b46d97e5301793824e5941dd2be')
'dist' finished successfully (0.006s)
---------------

The default compression format is http://www.bzip.org/[bzip2]. It may be changed to http://www.gzip.org/[gzip] by using the symbol 'gz':

[source,python]
---------------
import Scripting
Scripting.g_gz = 'gz'
---------------

Or 'zip' for http://en.wikipedia.org/wiki/ZIP_%28file_format%29[zip] files:

[source,python]
---------------
import Scripting
Scripting.g_gz = 'zip'
---------------

=== Splitting a project into several files

Although a Waf project must contain a top-level wscript file, the contents may be split into several sub-project files. We will now illustrate this concept on a small project:

[source,shishell]
---------------
.
|-- src
|   `-- wscript
`-- wscript
---------------

The commands in the top-level wscript will call the same commands from a subproject wscript file by calling a context method named 'recurse'.

[source,python]
---------------
top = '.'
out = 'build_directory'

def configure(ctx):
	print('→ configure from the top-level')
	ctx.recurse('src')

def print_ping(ctx):
	print('→ ping from the top-level')
	ctx.recurse('src')
---------------

Since the folder 'src' is not a redistributable project, it is not necessary to duplicate the variables _top_ and _out_.

[source,python]
---------------
def configure(ctx):
	print('→ configure from src')

def print_ping(ctx):
	print('→ ping from src')
---------------

Upon execution, the results will be:

[source,shishell]
---------------
$ cd /tmp/smallproject

$ waf configure print_ping
→ configure from the top-level
→ configure from src
'configure' finished successfully (0.080s)
→ ping from the top-level
→ ping from src
'print_ping' finished successfully (0.009s)
---------------

=== Building, cleaning, installing and uninstalling a project

The 'build' command is used for building the actual software. Starting again from the project file '/tmp/smallfolder/wscript':

[source,python]
---------------
top = '.'
out = 'build_directory'

def configure(ctx):
	print('→ configure from the top-level')

def build(ctx):
	print('building the software')
---------------

Without surprize, the execution output will look like the following:

[source,shishell]
---------------
$ cd /tmp/smallproject

$ waf
Project not configured (run 'waf configure' first)

$ waf configure
→ configure from the top-level
'configure' finished successfully (0.001s)

$ waf build
Waf: Entering directory `/tmp/smallproject/build_directory'
building the software
Waf: Leaving directory `/tmp/smallproject/build_directory'
'build' finished successfully (0.004s)
---------------

Since the command `waf build` is executed very often, a shortcut is provided to call it implicitely:

[source,shishell]
---------------
$ waf
Waf: Entering directory `/tmp/smallproject/build_directory'
building the software
Waf: Leaving directory `/tmp/smallproject/build_directory'
---------------

The Waf commands +build+, +clean+, +install+, +uninstall+ are shortcuts for calling `waf build` with different internal options. They all require the presence of the _build_ function in the scripts.

[source,shishell]
---------------
$ waf build install uninstall clean
Waf: Entering directory `/tmp/smallproject/build_directory'
building the software
Waf: Leaving directory `/tmp/smallproject/build_directory'
'build' finished successfully (0.004s)
Waf: Entering directory `/tmp/smallproject/build_directory'
building the software
Waf: Leaving directory `/tmp/smallproject/build_directory'
'install' finished successfully (0.003s)
Waf: Entering directory `/tmp/smallproject/build_directory'
building the software
Waf: Leaving directory `/tmp/smallproject/build_directory'
'uninstall' finished successfully (0.002s)
building the software
'clean' finished successfully (0.002s)
---------------

The meaning of the commands is the following:

. +build:+ process the source code to create the object files
. +clean:+ remove the object files that were created during a build (unlike distclean, do not remove the configuration)
. +install:+ check that all object files have been generated and copy them on the system (programs, libraries, data files, etc)
. +uninstall:+ undo the installation, remove the object files from the system without touching the ones in the build directory

Object file creation and installation will be detailed in the next chapters.

=== Customizing the command-line options

The Waf script provides various default command-line options, which may be consulted by executing `waf --help`:

[source,shishell]
---------------
$ waf --help
waf [command] [options]

Main commands (example: ./waf build -j4)
  build    : builds the project
  clean    : removes the build files
  configure: configures the project
  dist     : makes a tarball for redistributing the sources
  distcheck: checks if the sources compile (tarball from 'dist')
  distclean: removes the build directory
  install  : installs the build files
  uninstall: removes the installed files

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -j JOBS, --jobs=JOBS  amount of parallel jobs (2)
  -k, --keep            keep running happily on independent task groups
  -v, --verbose         verbosity level -v -vv or -vvv [default: 0]
  --nocache             ignore the WAFCACHE (if set)
  --zones=ZONES         debugging zones (task_gen, deps, tasks, etc)
  -p, --progress        -p: progress bar; -pp: ide output
  --targets=COMPILE_TARGETS
                        build given task generators, e.g. "target1,target2"

  configuration options:
    --prefix=PREFIX     installation prefix (configuration) [default: '/usr/local/']

  installation options:
    --destdir=DESTDIR   installation root [default: '']
    -f, --force         force file installation
---------------

Accessing a command-line option is possible from any command. Here is how to access the value 'prefix':

[source,python]
---------------
top = '.'
out = 'build_directory'

def configure(ctx):

	import Options
	print('→ prefix is ' + Options.options.prefix)
---------------

Upon execution, the following will be observed:

[source,shishell]
---------------
$ waf configure
→ prefix is /usr/local/
'configure' finished successfully (0.001s)
---------------

To define project command-line options, a special command named 'set_options' may be defined in user scripts. This command will be called once before any other command executes.

[source,python]
---------------
top = '.'
out = 'build_directory'

def set_options(ctx):
	ctx.add_option('--foo', action='store', default=False, help='Silly test')

def configure(ctx):

	import Options
	print('→ the value of foo is %r' % Options.options.foo)
---------------

Upon execution, the following will be observed:

[source,shishell]
---------------
$ waf configure --foo=test
→ the value of foo is 'test'
'configure' finished successfully (0.001s)
---------------

The command context for set_options is a shortcut to access the optparse functionality. For more information on the optparse module, consult the http://docs.python.org/library/optparse.html[Python documentation]

