== Build copies and variants

A common scenario is to duplicate the outputs for a particular build phase. The copy may be performed into another build directory, or into subfolders of the same tree called variants.

=== Using several build folders

It is not possible to use several Waf instances concurrently over the same build folder. Yet, several Waf instances may use the project at the same time. For this, two options must be set:

. The environment variable `WAFCACHE`
. The build directory, using a command-line option

Here is an example for a simple project located in '/tmp/smallfolderr'`:

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

def configure(conf):
	pass

def build(bld):
	bld(rule='touch ${TGT}', target='foo.txt')
---------------

Upon execution, the results will be the following:

[source,shishell]
---------------
$ export WAFLOCK=.lock-debug <1>

$ waf distclean configure -b debug <2>
'distclean' finished successfully (0.002s)
'configure' finished successfully (0.001s)

$ waf
Waf: Entering directory `/tmp/smallproject/debug'
[1/1] foo.txt:  -> debug/default/foo.txt <3>
Waf: Leaving directory `/tmp/smallproject/debug'
'build' finished successfully (0.012s)

$ export WAFLOCK=.lock-release

$ waf distclean configure -b release
'distclean' finished successfully (0.001s)
'configure' finished successfully (0.176s)

$ waf
Waf: Entering directory `/tmp/smallproject/release' <4>
[1/1] foo.txt:  -> release/default/foo.txt
Waf: Leaving directory `/tmp/smallproject/release'
'build' finished successfully (0.034s)

$ tree -a
.
|-- .lock-debug <5>
|-- .lock-release
|-- debug
|   |-- .wafpickle-7
|   |-- c4che
|   |   |-- build.config.py
|   |   `-- default.cache.py
|   |-- config.log
|   `-- default
|       `-- foo.txt
|-- release
|   |-- .wafpickle-7
|   |-- c4che
|   |   |-- build.config.py
|   |   `-- default.cache.py
|   |-- config.log
|   `-- default
|       `-- foo.txt
`-- wscript
---------------

<1> The environment variable 'WAFLOCK' points at the configuration of the project in use.
<2> The lockfile is created during the configuration.
<3> The files are output in the build directory `debug`
<4> The configuration 'release' is used with a different lock file and a different build directory.
<5> The contents of the project directory contain the two lock files and the two build folders.

When waf is executed, it reads the variable 'WAFLOCK' on an internal variable, which may be modified programmatically:

[source,python]
---------------
import Options
Options.lockfile = '.lockfilename'
---------------

=== Defining variants

Using different build folders is very useful for checking at some point if a different configuration would compile properly. To create different kinds of builds at once, it is possible to use 'Waf variants' to predefine the configuration sets for specific output subdirectories.

We will now demonstrate the definition and the usage of two variants named 'default' and 'debug' respectively:

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

def configure(conf):
	conf.env.NAME = 'default'
	dbg = conf.env.copy() <1>
	rel = conf.env.copy()

	dbg.set_variant('debug') <2>
	conf.set_env_name('debug', dbg) <3>
	conf.setenv('debug') <4>
	# conf.check_tool('gcc') <5>
	conf.env.NAME = 'foo' <6>

	rel.set_variant('release') <7>
	conf.set_env_name('cfg_name', rel)
	conf.setenv('cfg_name')
	conf.env.NAME = 'bar'

def build(bld):
	bld(
		rule='echo ${NAME} > ${TGT}',
		target='test.txt')

	bld(
		rule='echo ${NAME} > ${TGT}',
		target='test.txt'<8>,
		env=bld.env_of_name('debug').copy()) <9>

	bld(
		rule='echo ${NAME} > ${TGT}',
		target='test.txt',
		env=bld.env_of_name('cfg_name').copy())
---------------

<1> Create a copy of the default data set.
<2> Set the copy to use the variant named 'debug': task using it will output their files into `out_bld/debug`
<3> Bind the configuration set to the configuration. The configuration set will be saved when the configuration terminates
<4> Replace 'conf.env' by our new debug configuration set
<5> Waf tools store their configuration data on conf.env, in this case the 'debug' configuration set, not in the default
<6> Store a variable on the 'debug' configuration set
<7> Define another variant called 'release'. The variant name and the configuration set name may be different.
<8> The argument 'env' is given to specify the task generator configuration set. The configuration set holds the variant definition.
<9> Environments may be retrieved by name from the build context object. It is recommended to make copies to avoid accidental data sharing.

Upon execution, an output similar to the following will be observed:

[source,shishell]
---------------
$ waf distclean configure build
'distclean' finished successfully (0.000s)
'configure' finished successfully (0.001s)
Waf: Entering directory `/tmp/smallproject/out_bld'
[1/3] test.txt:  -> out_bld/default/test.txt
[2/3] test.txt:  -> out_bld/debug/test.txt <1>
[3/3] test.txt:  -> out_bld/release/test.txt
Waf: Leaving directory `/tmp/smallproject/out_bld'
'build' finished successfully (0.020s)

$ tree out_bld/
out_bld/
|-- c4che
|   |-- cfg_name.cache.py <2>
|   |-- build.config.py
|   |-- debug.cache.py
|   `-- default.cache.py
|-- config.log
|-- debug
|   `-- test.txt
|-- default
|   `-- test.txt <3>
`-- release
    `-- test.txt

$ cat out_bld/default/test.txt out_bld/debug/test.txt out_bld/release/test.txt
default <4>
foo
bar
---------------

<1> The files are output in their respective variants
<2> The configuration sets are stored and retrieved by names, which must be valid filenames without blanks.
<3> The tasks output their files in the relevant variant
<4> The file contents are different and correspond to the configuration sets used

NOTE: As a general rule, tasks created for a particular variant should not share data with the tasks from another variant.

=== Cloning task generators

A cloning scheme is provided for duplicating task generators for several variants easily. A general design pattern is to duplicate the task generators for the desired variants immediately before the build starts. Here is an example, also available in the folder `demos/cxx` of the Waf distribution.

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

import Options

def set_options(opt):
	opt.add_option('--build_kind', action='store', default='debug,release', help='build the selected variants') <1>

def configure(conf): <2>
	for x in ['debug', 'release']:
		env = conf.env.copy()
		env.set_variant(x)
		conf.set_env_name(x, env)

def build(bld):

	bld(rule='touch ${TGT}', target='foo.txt') <3>

	for obj in bld.all_task_gen[:]: <4>
		for x in ['debug', 'release']:
			cloned_obj = obj.clone(x) <5>
			kind = Options.options.build_kind
			if kind.find(x) < 0:<6>
				cloned_obj.posted = True
		obj.posted = True <7>
---------------

<1> Add a command-line option for enabling or disabling the 'release' and 'debug' builds
<2> The configuration will create the 'release' and 'debug' configuration sets, bound to a variant of the same names
<3> Targets are declared normally for the default variant
<4> A copy of the existing task generators is created to avoid the creation of an infinite loop (new task generator instances get added to that list)
<5> Clone a task generator for the configuration set 'debug' or 'release'. Making task generator clones is a cheap operation compared to duplicating tasks.
<6> Look at the command-line arguments, and disable the unwanted variant(s)
<7> Disable the original task generator for the default configuration set (do not use it).

Some task generators are use indexed attributes to generate unique values which may cause unnecessary rebuilds if the scripts change. To avoid problems, it is a best practice to create the task generators for the default configuration set first. Also, the method 'clone' is not a substitute for creating instances for lots of nearly identical task generators. In such a situation, it will be better to use one task generator to create lots of tasks. As a reminder, creating task generator clones for the same variant will lead to build errors.

WARNING: Do not create task generator clones for the same variant or for the same configuration set.

