<?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-builders-commands"
         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>Not Writing a Builder:  the &Command; Builder</title>

  <!-- cons reference documentation:

  =head2 The C<Command> method


  The C<Command> method is called as follows:

    Command $env <target>, <inputs>, <build action>;

  The target is made dependent upon the list of input files specified, and the
  inputs must be built successfully or Cons will not attempt to build the
  target.

  To specify a command with multiple targets, you can specify a reference to a
  list of targets. In Perl, a list reference can be created by enclosing a
  list in square brackets. Hence the following command:

    Command $env ['foo.h', 'foo.c'], 'foo.template', q(
  	gen %1
    );

  could be used in a case where the command C<gen> creates two files, both
  F<foo.h> and F<foo.c>.

  -->

  <para>

  Creating a &Builder; and attaching it to a &consenv;
  allows for a lot of flexibility when you
  want to re-use actions
  to build multiple files of the same type.
  This can, however, be cumbersome
  if you only need to execute one specific command
  to build a single file (or group of files).
  For these situations, &SCons; supports a
  &f-link-Command; builder that arranges
  for a specific action to be executed
  to build a specific file or files.
  This looks a lot like the other builders
  (like &b-link-Program;, &b-link-Object;, etc.),
  but takes as an additional argument
  the command to be executed to build the file:

  </para>

  <scons_example name="builderscommands_ex1">
     <file name="SConstruct" printme="1">
env = Environment()
env.Command('foo.out', 'foo.in', "sed 's/x/y/' &lt; $SOURCE > $TARGET")
     </file>
     <file name="foo.in">
foo.in
     </file>
  </scons_example>

  <para>

  When executed,
  &SCons; runs the specified command,
  substituting &cv-link-SOURCE; and &cv-link-TARGET;
  as expected:

  </para>

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

  <para>

  This is often more convenient than
  creating a &Builder; object
  and adding it to the &cv-link-BUILDERS; variable
  of a &consenv;.

  </para>

  <para>

  Note that the action you specify to the
  &Command; &Builder; can be any legal &SCons; &Action;,
  such as a Python function:

  </para>

  <scons_example name="builderscommands_ex2">
     <file name="SConstruct" printme="1">
env = Environment()

def build(target, source, env):
    # Whatever it takes to build
    return None

env.Command('foo.out', 'foo.in', build)
     </file>
     <file name="foo.in">
foo.in
     </file>
  </scons_example>

  <para>

  Which executes as follows:

  </para>

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

  <para>

  &cv-link-SOURCE; and &cv-link-TARGET; are expanded
  in the source and target as well:

  </para>

  <!-- NOTE: this used to be an scons_example, but was not complete and
       didn't have a matching scons_output, which meant problems were not
       detected. The style of this line is now reused in the last example
       of the section to make sure it's actually tested
       (see issue #2905 - which was closed prematurely).
  -->
  <sconstruct>
env.Command('${SOURCE.base}.out', File('foo.in'), build)
  </sconstruct>

  <para>

  Which does the same thing as the previous example, but allows you
  to write a more generic rule for transforming the source filename
  to the target filename, since unlike regular Builders,
  &Command; does not have any built-in rules for that.

  </para>

  <tip>
    <title>Sidebar: Node Special Attributes</title>

  <para>

  The example uses a <firstterm>Node special attribute</firstterm>
  (<literal>.base</literal>, the file without its suffix),
  a concept which has not been introduced yet,
  but will appear in several subsequent examples
  (see details in the Reference Manual section
  <emphasis>Substitution: Special Attributes</emphasis>).
  Due to the quirks of &SCons;' deferred evaluation scheme,
  node special attribues do not currently work
  in source and target arguments <emphasis>if</emphasis> the
  replacement is a string (like <literal>'foo.in'</literal>).
  They do work fine in strings describing actions.
  You can give &SCons; a little help by
  manually converting the filename string to a Node
  (see <xref linkend="sect-creating-nodes"/>),
  which is the approach used in the example.

  </para>
  </tip>

  <para>

  The method described in
  <xref linkend="sect-controlling-build-output"/> for controlling
  build output works well when used with pre-defined builders which
  have pre-defined <literal>*COMSTR</literal> variables for that purpose,
  but that is not the case when calling &f-Command;,
  where &SCons; has no specific knowledge of the action ahead of time.
  If the action argument to &f-Command; is not already an &Action; object,
  it will construct one for you with suitable defaults,
  which include a message based on the type of action.
  However, you can also construct the &Action; object yourself
  to pass to &f-Command;, which gives you much more control.
  Using the <parameter>action</parameter> keyword can also help
  make such lines easier to read.
  Here's an evolution of the example from above showing this approach:

  </para>

  <scons_example name="builderscommands_ex3">
     <file name="SConstruct" printme="1">
env = Environment()

def build(target, source, env):
    # Whatever it takes to build
    return None

act = Action(build, cmdstr="Building ${TARGET}")
env.Command('${SOURCE.base}.out', File('foo.in'), action=act)
     </file>
     <file name="foo.in">
foo.in
     </file>
  </scons_example>

  <para>

  Which executes as follows:

  </para>

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

</chapter>
