Chapter 30. Customizing Default Tools

Tools are used to execute actions. Examples are a compiler and a linker. Each tool must be executed with specific arguments. But the recipe attempts to be portable, thus not include the literal command to be executed. The mechanism described in this chapter takes care of translating the generic action into invoking a specific tool with its arguments.

It all starts with an action. Let us use the C compile action as an example. In the default recipe a default C compile action is defined. This one works for most C compilers that take arguments like the Unix "cc" command. If another kind of compiler is to be used, the $C_COMPILE_ACTION variable is set to the name of the action to be used, for example "compile_msvc". The default compile action will check the $C_COMPILE_ACTION variable and invoke the "compile_msvc" action instead of using its generic compile command.

During startup the default recipe will check for existence of tools for C and C++. A specific sequence of tools is checked for, depending on the platform. Thus in MS-Windows "msvc" will be checked for, while on Unix this doesn't happen. This is implemented with a Python module. For msvc it is "tools/msvc.py".

Each tool module defines an exists() function. It contains a check if the tool can be found. Mostly this is done by looking for a certain executable program. The msvc tool searches for "cl" (the compiler) and "vcvars32" (a command to start using the MSVC command line tools).

If a tool is detected, the actions for it are defined. This is always done, also when another tool was already detected. This allows the user to invoke specific actions or switch toolchain where he wants to. For MSVC the "compile_msvc" action is defined. That is like the normal "compile" action but with MSVC specific arguments.

The first tool that is detected will be used by default. The use_actions() function of the tool is invoked, which sets a number of variables, such as $C_COMPILE_ACTION, to the name of the action to be used. As mentioned above, the result will be that the generic actions will invoke the tool-specific action.

Building in a different way

The default build action works for object files generated from C source files. It might also work for other object files, in that case you do not need to do anything.

When building needs to be done in a different way, depending on the kind of source files used, add a "buildaction" attribute to the object file. This will make the default build action invoke the action specified with "buildaction" instead of the default build commands. Sources without a "buildaction" attribute do not influence this choice.

Note that it is not possible to have two different "buildaction" attributes on the sources of the build action. If you want to handle this set the $BUILD_ACTION variable to the build action that can handle this.

If a tool needs to do building for C and C++ files differently, set the $C_BUILD_ACTION and/or $CXX_BUILD_ACTION variables to the action that will do the building.

Adding A New Tool

You need to write a Python module and place it in the "tools" directory. Copy one of the existing tool files to start with. Then make changes to the functions:

exists()

Return True if the tool can be located. This is mostly done by simply checking for an executable program with the program_path() function. But you might do something more complicated, such as running the program with a "--version" argument and check the output.

define_actions()

Define the actions that this tool can accomplish. Each action name should be formed from the basic action name with "_toolname" appended. Thus a compile action for the MSVC compiler uses the action name "compile_msvc". You can define this action for several file types.

The action usually supports using variables, so that the user can modify them in a recipe. Some variables are generic and can be used by all tools, such as $CFLAGS. Your tool may need to translate it from the generic form to the tool-specific argument. For example, if $DEFINE contains "-DFOO=foo" you might have to translate this to "/D:FOO=foo".

You can also support specific variables for your tool. For example $MSVC is used to specify the name of the compiler. You give it a default value in the toplevel scope, but only when the user didn't do that already. The toplevel scope can be obtained from the Global module: "Global.globals".

use_actions(scope)

This function is called when the actions of the tool will be used as the default actions in "scope". When the tool is the first one found it will be called from the startup code. But the user may also use this to select a specific tool to be used in one recipe, or even one dependency.

Using A Specific Tool

The :usetool command can be used to specify a specific tool to be used in the current scope. When used in the toplevel recipe the tool becomes the default tool. When used in a child recipe the tool will be used in that recipe or by all actions invoked there. It can also be used in build commands, the tool will be used by invoked actions and dependencies.

Example:

        :usetool mingw
        :update prog_A
        :usetool msvc
        :update prog_B

This actually works by defining the tool-specific actions and defining variables such as $C_COMPILE_ACTION in the current scope.