|
AddingNewToolsToWaf
Description of how to add support for a language or a compiler
IntroductionWaf already supports quite a number of tools (gcc, g++, java, python, intltool etc. etc.) that compile software. If you happen to use a tool that waf does not support yet this page describes how to add that support. Once you have written the code please file an issue in our Issue tracker and attach the patch, or send it to the development mailing list Waf tool definitionA Waf tool is a file located in the wafadmin/Tools folder. It is used to extend the Waf behavior in the following areas:
The Waf tools contain python code in no particular order, except for the function detect(conf). It is executed during the configuration, when the code conf.check_tool('tool_name') is called Writing Waf tools1. Detecting the project settingsThe body of the function detect(conf) is used to detect the configuration:
2. Extending the Configuration classThe conf object can be extended by attaching new methods to it (conf.hook). The methods usually throw ConfigurationError if something fails. The purpose is to extend the configuration routines and to limit the amount of import to write. Here is a quick example (the module checks.py provides several useful examples): def check_dummy(self):
return True
def detect(conf):
"attach the checks to the conf object"
conf.hook(check_dummy)
# now use the hook attached to the conf instance
result = conf.check_dummy()3. Defining new compilation rulesWaf separates the high-level interfaces (declaring programs, shared libraries) from the low-level tasks (compile this file and add the results to the files to link). The user script almost never access the low-level apis, but this is needed for defining new kinds of file processing. The elements needed for defining new compilation rules are the following (they are not all needed):
a. Defining new TasksWaf decomposes the file transformations into units of change. These units are called tasks, they can be linked to other tasks, and they can be run in parallel or one by one (depending on the constraints). The Task objects can be created in three ways:
The most popular way is to use the simple_task_type: Task.simple_task_type('task_name', '${COMPILER} ${SRC} > ${TGT}', color='BLUE', prio=40)The variables such as ${COMPILER} represent environment variables, while the SRC and TGT are special variables representing the input and output files Another optional way is to define your own 'builder' function, then call task_type_from_func: Task.task_type_from_func('task_name', builder_func, color='CYAN', prio=120)b. Extending the class task_genThe classes instantiated when calling bld.create_obj are instances of Object.py::task_gen. That class provides settings for calling methods (adding, removing, changing methods is possible), and for declaring the order in which to call the code. The core of the task_gen class is the method apply which takes the methods to execute, performs a topological sort on them, and calls them one by one (code execution in parallel would even be possible). Here is how to declare a new method on task_gen:
The new methods may perform the following operations:
As an example: from Object import taskgen, after, before, feature
@taskgen
@after('apply_core')
@before('install_target')
@feature('cc')
def special_install(self):
if not getattr(self, "special", None): return
print "disabling the regular installation"
self.inst_var = 0c. Adding file extensionsThe class task_gen usually executes the method apply_core which is used to process the source files (the "source" attribute). The source files are transformed one by one into Node objects and are added to a temporary list. The list is then processed using a mapping file_extension -> method Here is how to declare a new extension that process files of extension ".coin" into ".cpp" files: from Object import extension
@taskgen
@extension('.coin')
def coin_file(self, node):
out_source = node.change_ext('.cpp')
tsk = self.create_task('task_name')
tsk.set_inputs(node)
tsk.set_outputs(out_source)
# the out file is to be processed as a cpp file
self.allnodes.append(out_source)4. Implementation detailsInstead of adding types, subtypes, and extensions through inheritance on task_gen (msvc.py), the following scheme is now used:
Here are a few examples, they only represent the ideas, not the actual code:
Using such meta-data adds enough abstraction without impacting performance (calling fewer methods) or flexibility (the methods can be added manually, or inheritance is still possible). |
Sign in to add a comment