17.8. Where To Put Your Custom Builders and Tools
Prev | Chapter 17. Extending SCons: Writing Your Own Builders | Next

## 17.8. Where To Put Your Custom Builders and Tools

The `site_scons` directories give you a place to put Python modules and packages that you can import into your `SConscript` files (at the top level), add-on tools that can integrate into SCons (in a `site_tools` subdirectory), and a `site_scons/site_init.py` file that gets read before any `SConstruct` or `SConscript` file, allowing you to change SCons's default behavior.

Each system type (Windows, Mac, Linux, etc.) searches a canonical set of directories for `site_scons`; see the man page for details. The top-level SConstruct's `site_scons` directory (that is, the one in the project) is always searched last, and its directory is placed first in the tool path so it overrides all others.

If you get a tool from somewhere (the SCons wiki or a third party, for instance) and you'd like to use it in your project, a `site_scons` directory is the simplest place to put it. Tools come in two flavors; either a Python function that operates on an `Environment` or a Python module or package containing two functions, `exists()` and `generate()`.

A single-function Tool can just be included in your `site_scons/site_init.py` file where it will be parsed and made available for use. For instance, you could have a `site_scons/site_init.py` file like this:

    def TOOL_ADD_HEADER(env):
        """A Tool to add a header from $HEADER to the source file"""
        add_header = Builder(
            action=['echo "$HEADER" > $TARGET', 'cat $SOURCE >> $TARGET']
        )
        env.Append(BUILDERS={'AddHeader': add_header})
        env['HEADER'] = ''  # set default value

and a `SConstruct` like this:

    # Use TOOL_ADD_HEADER from site_scons/site_init.py
    env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
    env.AddHeader('tgt', 'src')

The `TOOL_ADD_HEADER` tool method will be called to add the `AddHeader` tool to the environment.

A more full-fledged tool with `exists()` and `generate()` methods can be installed either as a module in the file `site_scons/site_tools/toolname.py` or as a package in the directory `site_scons/site_tools/toolname`. In the case of using a package, the `exists()` and `generate()` are in the file `site_scons/site_tools/toolname/__init__.py`. (In all the above case `toolname` is replaced by the name of the tool.) Since `site_scons/site_tools` is automatically added to the head of the tool search path, any tool found there will be available to all environments. Furthermore, a tool found there will override a built-in tool of the same name, so if you need to change the behavior of a built-in tool, `site_scons` gives you the hook you need.

Many people have a collection of utility Python functions they'd like to include in their `SConscript` files: just put them in `site_scons/my_utils.py` or any valid Python module name of your choice. For instance, you can do something like this in `site_scons/my_utils.py` to add `build_id` and `MakeWorkDir` functions:

    from SCons.Script import *  # for Execute and Mkdir

    def build_id():
        """Return a build ID (stub version)"""
        return "100"

    def MakeWorkDir(workdir):
        """Create the specified dir immediately"""
        Execute(Mkdir(workdir))

And then in your `SConscript` or any sub-`SConscript` anywhere in your build, you can import `my_utils` and use it:

    import my_utils
    print("build_id=" + my_utils.build_id())
    my_utils.MakeWorkDir('/tmp/work')

You can put this collection in its own module in a `site_scons` and import it as in the example, or you can include it in `site_scons/site_init.py`, which is automatically imported (unless you disable site directories). Note that in order to refer to objects in the SCons namespace such as `Environment` or `Mkdir` or `Execute` in any file other than a `SConstruct` or `SConscript` you always need to do

    from SCons.Script import *

This is true of modules in `site_scons` such as `site_scons/site_init.py` as well.

You can use any of the user- or machine-wide site directories such as `~/.scons/site_scons` instead of `./site_scons`, or use the `--site-dir` option to point to your own directory. `site_init.py` and `site_tools` will be located under that directory. To avoid using a `site_scons` directory at all, even if it exists, use the `--no-site-dir` option.

Prev | Up | Next
17.7. Modifying a Builder by adding an Emitter | Home | Chapter 18. Not Writing a Builder: the `Command` Builder