Make your own build system with the Waf framework
Waf 1.6 features a new customization system for the Waf files, which means that the Waf libraries can be re-used easily without forcing the use of wscript files. Here is how to create a waf file that will print "Hello, world!" and stop:
waf configureThe resulting waf file will then exhibit the following lines of code:
--prelude=$'\tprint("Hello, world!")\n\tsys.exit(0)\n'
if __name__ == '__main__':Writing code in the prelude section is a bit complicated, but the execution can be delegated to a separate waf tool. For example, with a tool named bbdlib featuring a function named "start":
print("Hello, world!")
sys.exit(0)
from waflib import Scripting
Scripting.waf_entry_point(cwd, VERSION, wafdir)
./waf-light configure build --tools=$PWD/bbdlib.pyThe file bbdlib.py will be included in the resulting waf file as waflib/extras/bbdlib.py, so the resulting code has to use an import. Also, the original parameters (current working directory, waf version and waf directory) are propagated as they can be useful in the execution:
--prelude=$'\tfrom waflib.extras import bbdlib\n\tbbdlib.start(cwd, VERSION, wafdir)\n\tsys.exit(0)'
if __name__ == '__main__':
from waflib.extras import bbdlib
bbdlib.start(cwd, VERSION, wafdir)
sys.exit(0)
from waflib import Scripting
Scripting.waf_entry_point(cwd, VERSION, wafdir)
Here is for example a script that may be used to read files named "bbit":
import os, sys, impHere are a few points to keep in mind:
from waflib import Context, Options, Configure, Utils, Logs
def start(cwd, version, wafdir):
try:
os.stat(cwd + '/bbit')
except:
print('call from a folder containing "bbit"')
sys.exit(1)
Logs.init_log()
Context.waf_dir = wafdir
Context.top_dir = Context.run_dir = cwd
Context.out_dir = os.path.join(cwd, 'build')
Context.g_module = imp.new_module('wscript')
Context.g_module.root_path = os.path.join(cwd, 'bbit')
Context.Context.recurse = lambda x, y: getattr(
Context.g_module, x.cmd, Utils.nada)(x)
Context.g_module.configure = lambda ctx: ctx.load('g++')
Context.g_module.build = lambda bld: bld.objects(
source='main.c')
opt = Options.OptionsContext().execute()
do_config = 'configure' in sys.argv
try:
os.stat(cwd + '/build')
except:
do_config = True
if do_config:
Context.create_context('configure').execute()
if 'clean' in sys.argv:
Context.create_context('clean').execute()
if 'build' in sys.argv:
Context.create_context('build').execute()
- A module simulating a wscript file is created dynamically to simulate the execution of a top-level wscript file. This script attempts to re-use as much code as possible, but some configuration tests illustrate how to bypass all restrictions.
- Although a few variables are shared by several classes (Context.top_dir), the initialization is not required. One might want to subclass the BuildContext class and start with a nearly empty script.
- The commands configure, clean and build are implemented for the illustration. In practice it may be easier to create command subclasses for specific purposes.