<?xml version='1.0'?>

<!--
SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org)
SPDX-License-Identifier: MIT
SPDX-FileType: DOCUMENTATION

This file is processed by the bin/SConsDoc.py module.
-->

<!DOCTYPE sconsdoc [
    <!ENTITY % scons SYSTEM "../scons.mod">
    %scons;

    <!ENTITY % builders-mod SYSTEM "../generated/builders.mod">
    %builders-mod;
    <!ENTITY % functions-mod SYSTEM "../generated/functions.mod">
    %functions-mod;
    <!ENTITY % tools-mod SYSTEM "../generated/tools.mod">
    %tools-mod;
    <!ENTITY % variables-mod SYSTEM "../generated/variables.mod">
    %variables-mod;

]>

<chapter id="chap-add-method"
         xmlns="http://www.scons.org/dbxsd/v1.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">

<title>Extending &SCons;: Pseudo-Builders and the AddMethod function</title>

  <para>

  The &f-link-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;.

  </para>

  <para>

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

  </para>

  <scons_example name="addmethod_ex1">
     <file name="SConstruct" printme="1">
def install_in_bin_dirs(env, source):
    """Install source in both bin directories"""
    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='__ROOT__/usr/bin', LOCALBIN='#install/bin')
env.AddMethod(install_in_bin_dirs, "InstallInBinDirs")
env.InstallInBinDirs(Program('hello.c'))  # installs hello in both bin directories
     </file>
     <file name="hello.c">
int main() { printf("Hello, world!\n"); }
     </file>
  </scons_example>

  <para>
  This produces the following:
  </para>

  <scons_output example="addmethod_ex1" suffix="1">
    <scons_output_command>scons -Q /</scons_output_command>
  </scons_output>

  <para>

  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.

  </para>

  <scons_example name="addmethod_ex2">
     <file name="SConstruct" printme="1">
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')
     </file>
     <file name="test_stuff.c">
int main() { printf("Hello, world!\n"); }
     </file>
     <file name="res.rc">
res.rc
     </file>
  </scons_example>

  <para>
  This produces the following on Linux:
  </para>

  <scons_output example="addmethod_ex2" suffix="1">
    <scons_output_command>scons -Q</scons_output_command>
  </scons_output>

  <para>
  And the following on Windows:
  </para>

  <scons_output example="addmethod_ex2" os="win32" suffix="2">
    <scons_output_command>scons -Q</scons_output_command>
  </scons_output>

  <para>
  Using &AddMethod; is better than just adding an instance method
  to a &consenv; because it gets called as a proper method,
  and because &AddMethod; provides for copying the method
  to any clones of the &consenv; instance.
  </para>

</chapter>
