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