waf

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

tutorial.rst (7319B)


      1 Waf tutorial
      2 ============
      3 
      4 Waf is a piece of software used to help building software projects.
      5 The goal of this tutorial is to provide a quick overview of how to set up
      6 the scripts for a project using Waf.
      7 
      8 Waf scripts and commands
      9 ------------------------
     10 
     11 A software typically has *source files* which are kept in a version control system (git, subversion, etc),
     12 and *build scripts* (Makefiles, ..) which describe what to do with those files. A few *build files* are usually
     13 obtained after transforming the *source files*, but they are optional. The build scripts in Waf are files named 'wscript'.
     14 
     15 In general, a project will consist of several phases:
     16 
     17 * configure: configure the project, find the location of the prerequisites
     18 * build: transform the source files into build files
     19 * install: install the build files
     20 * uninstall: uninstall the build files
     21 * dist: create an archive of the source files
     22 * clean: remove the build files
     23 
     24 Each phase is modelled in the wscript file as a python function which takes as argument an instance of :py:class:`waflib.Context.Context`.
     25 Let's start with a new wscript file in the directory '/tmp/myproject'::
     26 
     27 	def configure(cnf):
     28 		print("configure!")
     29 
     30 	def build(bld):
     31 		print("build!")
     32 
     33 We will also use a Waf binary file, for example waf-2.0.0, which we will copy in the project directory::
     34 
     35 	$ cd /tmp/myproject
     36 	$ wget https://waf.io/waf-2.0.0
     37 
     38 To execute the project, we will simply call the command as an argument to ``waf``::
     39 
     40 	$ python ./waf-2.0.0 configure build
     41 	configure!
     42 	build!
     43 
     44 Targets
     45 -------
     46 
     47 An important part of the build system is to declare the creation of targets. Here is a very simple example::
     48 
     49 	def build(bld):
     50 		tg = bld(rule='cp ${SRC} ${TGT}', source='wscript', target='foo.txt')
     51 		bld(rule='cp ${SRC} ${TGT}', source='foo.txt', target='bar.txt')
     52 
     53 The call ``bld(..)`` creates an object called *task generator*, which is used to create *tasks* which will actually
     54 call the command ``cp``. The commands are not executed unless all the scripts have been read, which is important
     55 for computing the build order.
     56 
     57 The expressions *${SRC}* and *${TGT}* are shortcuts to avoid repeating the file names. More shortcuts can be defined
     58 by using the *${}* symbol, which reads the values from the attribute bld.env::
     59 
     60 	def build(bld):
     61 		bld.env.MESSAGE = 'Hello, world!'
     62 		bld(rule='echo ${MESSAGE}', always=True)
     63 
     64 The bld object is an instance of :py:class:`waflib.Build.BuildContext`, its *env* attribute is an instance :py:class:`waflib.ConfigSet.ConfigSet`.
     65 This object is also accessible as an attribute on the `configure()` method's `cnf` parameter. Therefore, values can be shared/stored/loaded easily:
     66 
     67 	def configure(cnf):
     68 		cnf.env.MESSAGE = 'Hello, world!'
     69 
     70 	def build(bld):
     71 		bld(rule='echo ${MESSAGE}', always=True)
     72 
     73 Scripts and Tools
     74 -----------------
     75 
     76 To let a script use a script from a subdirectory, the method :py:meth:`waflib.Context.Context.recurse` has to be used with
     77 the relative path to the folder containing the wscript file. For example, to call the function *build* in the script ``src/wscript``,
     78 one should write::
     79 
     80 	def build(bld):
     81 		bld.recurse('src')
     82 
     83 The support for specific languages and compilers is provided through specific modules called *Waf tools*. The tools are
     84 similar to wscript files and provide functions such as *configure* or *build*. Here is a simple project for the C programming language::
     85 
     86 	def options(opt):
     87 		opt.load('compiler_c')
     88 	def configure(cnf):
     89 		cnf.load('compiler_c')
     90 	def build(bld):
     91 		bld(features='c cprogram', source='main.c', target='app')
     92 
     93 The function *options* is another predefined command used for setting command-line options. Its argument is an instance of :py:meth:`waflib.Options.OptionsContext`. The tool *compiler_c* is provided for detecting if a C compiler is present and to set various variables such as ``cnf.env.CFLAGS``.
     94 
     95 The task generator declared in *bld* does not have a *rule* keyword, but a list of *features* which is used to reference methods that will call the appropriate rules. In this case, a rule is called for compiling the file, and another is used for linking the object files into the binary *app*. Other tool-dependent features exist such as *javac*, *cs*, or *tex*.
     96 
     97 A C and C++ project
     98 -------------------
     99 
    100 Here is a script for a more complicated project::
    101 
    102 	def options(opt):
    103 		opt.load('compiler_c compiler_cxx')
    104 	def configure(cnf):
    105 		cnf.load('compiler_c compiler_cxx')
    106 		cnf.check(features='cxx cxxprogram', lib=['m'], cflags=['-Wall'], defines=['var=foo'], uselib_store='M')
    107 	def build(bld):
    108 		bld(features='c cshlib', source='b.c', target='mylib')
    109 		bld(features='c cxx cxxprogram', source='a.c main.cpp', target='app', use=['M','mylib'], lib=['dl'])
    110 
    111 The method :py:func:`waflib.Tools.c_config.check` executes a build internally to check if the library ``libm`` is present on the operating system.
    112 It will then define variables such as:
    113 
    114 * ``cnf.env.LIB_M = ['m']``
    115 * ``cnf.env.CFLAGS_M = ['-Wall']``
    116 * ``cnf.env.DEFINES_M = ['var=foo']``
    117 
    118 By stating ``use=['M', 'mylib']``, the program *app* is going to inherit all the *M* variables defined
    119 during the configuration. The program will also use the library *mylib* and both the build order and the dependencies
    120 will be modified so that *mylib* is linked before *app*.
    121 
    122 The ``use`` attribute is also working for other languages such as Java (dependencies between jar files) or C# (dependencies between assemblies).
    123 
    124 Project-specific extensions
    125 ---------------------------
    126 
    127 The *feature* keyword is a high-level reference to existing Waf methods.
    128 For example, the **c** feature will add the method :py:func:`waflib.Tools.ccroot.apply_incpaths` for execution.
    129 To add a new method that will add the task generator path to the include path for all C targets,
    130 one may use such a declaration::
    131 
    132 	from waflib import Utils
    133 	from waflib.TaskGen import feature, before_method
    134 	@feature('c')
    135 	@before_method('apply_incpaths')
    136 	def add_current_dir_to_includes(self):
    137 		self.includes = Utils.to_list(self.includes)
    138 		self.includes.append(self.path)
    139 
    140 	def build(bld):
    141 		tg = bld(features='c', source='main.c', target='app')
    142 
    143 The *feature* methods are bound to the :py:class:`waflib.TaskGen.task_gen` class, which is the class of the
    144 object *tg* in the example. New features can be declared in the same manner::
    145 
    146 	from waflib.TaskGen import feature, after_method
    147 	@feature('debug_tasks')
    148 	@after_method('apply_link')
    149 	def print_debug(self):
    150 		print('tasks created %r' % self.tasks)
    151 
    152 	def build(bld):
    153 		tg = bld(features='c cprogram debug_tasks', source='main.c', target='app')
    154 
    155 The declaration can be made more user-friendly by binding new methods to the context classes::
    156 
    157 	from waflib.Build import BuildContext
    158 	def enterprise_program(self, *k, **kw):
    159 		kw['features'] = 'c cprogram debug_tasks'
    160 		return self(*k, **kw)
    161 	BuildContext.enterprise_program = enterprise_program
    162 
    163 	def build(bld):
    164 		# no feature line
    165 		bld.enterprise_program(source='main.c', target='app')
    166 
    167 The support code may be turned into a Waf tool by moving it to a separate file.
    168 To ease the deployment, the new Waf tool can even be added to the waf file (see https://gitlab.com/ita1024/waf/blob/master/README.md#L20).
    169 
    170 Conclusion
    171 ----------
    172 
    173 This concludes the tutorial. For more information consult the apis, the Waf book and the examples.
    174