Sophie

Sophie

distrib > Mandriva > 2009.1 > x86_64 > media > contrib-backports > by-pkgid > 5449138d6297d4beefc46ffe46a8c51a > files > 33

waf-1.5.11-1mdv2009.1.noarch.rpm

<?xml version='1.0'?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
<chapter id="execution">
	<title>The scripting system</title>
	<section id="setup">
		<title>Setting up a project</title>
		<para>
		The Waf projects are structured based on the following concepts:
			<itemizedlist>
				<listitem>Source directory: directory containing the source files that will be packaged and redistributed to other developers or to end users</listitem>
				<listitem>Build directory: directory containing the files generated by the project (configuration sets, build files, logs, etc)</listitem>
				<listitem>System files: files and folders which do not belong to the project (operating system files, etc)</listitem>
			</itemizedlist>
		</para>

		<para>
		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:
			<itemizedlist>
				<listitem>srcdir: string representing the source directory. In general, srcdir is set to '.', except for some proprietary projects where to wscript cannot be added to the top-level, srcdir may be set to '../..' or even some other folder such as '/checkout/perforce/project'</listitem>
				<listitem>blddir: 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 '..'</listitem>
				<listitem>configure: function called for setting up a project (also known as a 'Waf command')</listitem>
			</itemizedlist>
		The Waf script will always look for one valid top-level project file first before considering other project files.
		</para>

		<para>
		Now let us create a new Waf project in the folder <emphasis>/tmp/smallproject</emphasis>. The first step is to write a wscript file in <emphasis>/tmp/smallproject/wscript</emphasis> with the following contents:
			<programlisting language="python">
srcdir = '.'
blddir = 'build_directory'

def configure(ctx):
    print('-> configuring the project')
			</programlisting>
		</para>

		<para>
		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):
			<programlisting language="sh">
$ cd /tmp/smallproject <co id="init1-co" linkends="initl"/>
$ tree
.
`-- wscript

$ waf configure <co id="init2-co" linkends="init2"/>
-> configuring the project
'configure' finished successfully (0.021s)

$ tree
.
|-- build_directory/ <co id="init3-co" linkends="init3"/>
|   |-- c4che/ <co id="init4-co" linkends="init4"/>
|   |   |-- build.config.py <co id="init5-co" linkends="init5"/>
|   |   `-- default.cache.py <co id="init6-co" linkends="init6"/>
|   |-- config.log <co id="init7-co" linkends="init7"/>
|   `-- default/ <co id="init8-co" linkends="init8"/>
|--.lock-wscript <co id="init9-co" linkends="init9"/>
`-- wscript
			</programlisting>

			<calloutlist>
				<callout arearefs="init1-co" id="init1"><para>To configure the project, go to the folder containing the top-level project file</para></callout>
				<callout arearefs="init2-co" id="init2"><para>The execution is called by calling "waf configure"</para></callout>
				<callout arearefs="init3-co" id="init3"><para>The build directory was created</para></callout>
				<callout arearefs="init4-co" id="init4"><para>The configuration data is stored in the folder c4che/</para></callout>
				<callout arearefs="init5-co" id="init5"><para>The command-line options and environment variables in use are stored in this file</para></callout>
				<callout arearefs="init6-co" id="init6"><para>The user configuration set is stored in this file</para></callout>
				<callout arearefs="init7-co" id="init7"><para>Configuration log (duplicate of the output generated during the configuration)</para></callout>
				<callout arearefs="init8-co" id="init8"><para>Directory for the generated files (none so far)</para></callout>
				<callout arearefs="init9-co" id="init9"><para>Lock file pointing at the relevant project file and build directory</para></callout>
			</calloutlist>

		</para>
	</section>

	<section id="commands">
		<title>Adding project commands</title>
		<para>
			In the last section, we have see the use of the command <emphasis>configure</emphasis> to initialize a project. Waf commands are special functions declared in the top-level project file and which may be called explicitely by <emphasis>waf commandname</emphasis>. Let us create two new commands <emphasis>print_ping</emphasis> and <emphasis>print_pong</emphasis> in the project created previously (/tmp/smallproject/)

			<programlisting language="python">
srcdir = '.'
blddir = 'build_directory'

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

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

def print_pong(ctx):
	print(' pong!')
			</programlisting>

			Waf commands always take a single parameter called the <emphasis>context</emphasis>. To execute the new commands:

			<programlisting language="sh">
$ 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)
			</programlisting>


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

			<programlisting language="sh">
$ 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)
			</programlisting>


			Waf commands may be repeated several times too:

			<programlisting language="sh">
$ 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)
			</programlisting>

		</para>
	</section>


	<section id="distclean">
		<title>Cleaning up a project</title>
		<para>
			Waf itself comes with a predefined command called <emphasis>distclean</emphasis> which removes the build directory and the lock file. After calling cleaning a project, it is necessary to configure it once again.

			<programlisting language="sh">
$ 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)
			</programlisting>
		</para>

		<para>
			It is possible to override the behaviour of <emphasis>distclean</emphasis> by redefining it in the wscript file. For example, the following will cause it to avoid removing the build files.
			<programlisting language="python">
srcdir = '.'
blddir = 'build_directory'

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

def distclean(ctx):
	print(' Not cleaning anything!')
			</programlisting>

			Upon execution:

			<programlisting language="sh">
$ waf distclean
 not cleaning anything!
'distclean' finished successfully (0.000s)
			</programlisting>
		</para>

	</section>

	<section id="dist">
		<title>Packaging a project</title>
		<para>
			The command <emphasis>dist</emphasis> is another predefined utility which is used to create an archive of the project. By using the script presented previously:

			<programlisting language='python'>
srcdir = '.'
blddir = 'build_directory'

def configure(ctx):
    print('-> configuring the project')
			</programlisting>

			Execute the command 'dist' to get:

			<programlisting language="sh">
$ 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)
			</programlisting>
		</para>

		<para>
			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:

			<programlisting language="python">
APPNAME='webe'
VERSION='2.0'

srcdir = '.'
blddir = 'build_directory'

def configure(ctx):
    print('-> configuring the project')
			</programlisting>

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

			<programlisting language='sh'>
$ waf dist
New archive created: webe-2.0.tar.bz2 (sha='7ccc338e2ff99b46d97e5301793824e5941dd2be')
'dist' finished successfully (0.006s)
			</programlisting>

			<para>
			The default compression format is <ulink url="http://www.bzip.org/">bzip2</ulink>. It may be changed to <ulink url="http://www.gzip.org/">gzip</ulink> by using the following code:
				<programlisting language="python">
import Scripting
Scripting.g_gz = 'gz'
				</programlisting>
			</para>
		</para>

	</section>

	<section id="recursion">
		<title>Splitting a project into several files</title>
		<para>
			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:
			<programlisting language="shell">
.
|-- src
|   `-- wscript
`-- wscript
			</programlisting>

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

			<programlisting language="python">
srcdir = '.'
blddir = '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')
			</programlisting>

			Since src/wscript is not to point to a single project, it is not necessary to duplicate the srcdir and blddir variables.

			<programlisting language="python">
def configure(ctx):
    print('-> configure from src')

def print_ping(ctx):
    print('-> ping from src')
			</programlisting>

			Upon execution, the results will be:

			<programlisting language="shell">
$ 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)
			</programlisting>

		</para>
	</section>


	<section id="build">
		<title>Building, cleaning, installing and uninstalling a project</title>
		<para>
			The Waf command is used for building the actual software. Let's start again from the project file /tmp/smallfolder/wscript:

			<programlisting language="python">
srcdir = '.'
blddir = 'build_directory'

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

def build(ctx):
    print('building the software')
			</programlisting>

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

			<programlisting language="sh">
$ 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)
			</programlisting>
			Since the command <emphasis>waf build</emphasis> is executed very often, a shortcut is provided to call it implicitely:

			<programlisting language="sh">
$ waf
Waf: Entering directory `/tmp/smallproject/build_directory'
building the software
Waf: Leaving directory `/tmp/smallproject/build_directory'
			</programlisting>
		</para>

		<para>

			The Waf commands <emphasis>build, clean, install, uninstall</emphasis> are shortcuts to call <emphasis>build</emphasis> with different internal options.

			<programlisting language="sh">
$ 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)
			</programlisting>

			The meaning of the commands is the following:

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

			Object file creation and installation will be detailed in the next chapters.
		</para>

	</section>

	<section id="options">
		<title>Customizing the command-line options</title>
		<para>
			The Waf script provides various default command-line options, which may be consulted by executing <emphasis>waf --help</emphasis>:
			<programlisting language="sh">
$ 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
			</programlisting>

			Accessing a command-line option is possible from any command. Here is how to access the value <emphasis>prefix</emphasis>:

			<programlisting language="python">
srcdir = '.'
blddir = 'build_directory'

def configure(ctx):

    import Options
    print('-> prefix is ' + Options.options.prefix)
			</programlisting>

			Upon execution, the following will be observed:
			<programlisting language="sh">
$ waf configure
-> prefix is /usr/local/
'configure' finished successfully (0.001s)
			</programlisting>

		</para>

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

			<programlisting language="python">
srcdir = '.'
blddir = '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)
			</programlisting>

			Upon execution, the following will be observed:
			<programlisting language="sh">
$ waf configure --foo=test
-> the value of foo is 'test'
'configure' finished successfully (0.001s)
			</programlisting>

			The command context for set_options is a shortcut to access the optparse functionality. For more information on the optparse module, consult the <ulink url="http://docs.python.org/library/optparse.html">Python documentation</ulink>.
		</para>

	</section>

</chapter>