Chapter 19. Extending SCons: Pseudo-Builders and the AddMethod function
Prev |  | Next

## Chapter 19. Extending SCons: Pseudo-Builders and the AddMethod function

The `AddMethod` function is used to add a method to an environment. It is typically used to add a "pseudo-builder," a function that looks like a Builder but wraps up calls to multiple other Builders or otherwise processes its arguments before calling one or more Builders.

In the following example, we want to install the program into the standard `/usr/bin` directory hierarchy, but also copy it into a local `install/bin` directory from which a package might be built:

    def install_in_bin_dirs(env, source):
        """Install source in both bin dirs"""
        i1 = env.Install("$BIN", source)
        i2 = env.Install("$LOCALBIN", source)
        return [i1[0], i2[0]]  # Return a list, like a normal builder

    env = Environment(BIN='/usr/bin', LOCALBIN='#install/bin')
    env.AddMethod(install_in_bin_dirs, "InstallInBinDirs")
    env.InstallInBinDirs(Program('hello.c'))  # installs hello in both bin dirs

This produces the following:

    % scons -Q /
    cc -o hello.o -c hello.c
    cc -o hello hello.o
    Install file: "hello" as "/usr/bin/hello"
    Install file: "hello" as "install/bin/hello"

A pseudo-builder is useful because it gives you more flexibility parsing arguments than you can get with a standard Builder. The next example shows a pseudo-builder with a named argument that modifies the filename, and a separate optional argument for a resource file (rather than having the builder figure it out by file extension). This example also demonstrates using the global `AddMethod` function to add a method to the global Environment class, so it will be available in all subsequently created environments.

    def BuildTestProg(env, testfile, resourcefile="", testdir="tests"):
        """Build the test program.

        Prepends "test_" to src and target and puts the target into testdir.
        If the build is running on Windows, also make use of a resource file,
        if supplied.
        """
        srcfile = f"test_{testfile}.c"
        target = f"{testdir}/test_{testfile}"
        if env['PLATFORM'] == 'win32' and resourcefile:
            resfile = env.RES(resourcefile)
            p = env.Program(target, [srcfile, resfile])
        else:
            p = env.Program(target, srcfile)
        return p

    AddMethod(Environment, BuildTestProg)

    env = Environment()
    env.BuildTestProg('stuff', resourcefile='res.rc')

This produces the following on Linux:

    % scons -Q
    cc -o test_stuff.o -c test_stuff.c
    cc -o tests/test_stuff test_stuff.o

And the following on Windows:

    C:\>scons -Q
    rc /nologo /fores.res res.rc
    cl /Fotest_stuff.obj /c test_stuff.c /nologo
    link /nologo /OUT:tests\test_stuff.exe test_stuff.obj res.res
    embedManifestExeCheck(target, source, env)

Using `AddMethod` is better than just adding an instance method to a construction environment because it gets called as a proper method, and because `AddMethod` provides for copying the method to any clones of the construction environment instance.

Prev |  | Next
Chapter 18. Not Writing a Builder: the `Command` Builder | Home | Chapter 20. Extending SCons: Writing Your Own Scanners