<?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-factories"
         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>Platform-Independent File System Manipulation</title>

  <para>

  &SCons; provides a number of platform-independent functions,
  called <literal>factories</literal>,
  that perform common file system manipulations
  like copying, moving or deleting files and directories,
  or making directories.
  These functions are <literal>factories</literal>
  because they don't perform the action
  at the time they're called,
  they each return an &Action; object
  that can be executed at the appropriate time.

  </para>

  <section id="sect-factories-intro">
  <title>Copying Files or Directories:  The &Copy; Factory</title>

    <para>

    Suppose you want to arrange to make a copy of a file,
    and don't have a suitable pre-existing builder.
    <footnote>
    <para>
    Unfortunately, in the early days of SCons design,
    we used the name &Copy; for the function that
    returns a copy of the environment,
    otherwise that would be the logical choice for
    a Builder that copies a file or directory tree
    to a target location.
    </para>
    </footnote>
    One way would be to use the &Copy; action factory
    in conjunction with the &Command; builder:

    </para>

    <scons_example name="factories_Copy1">
      <file name="SConstruct" printme="1">
Command("file.out", "file.in", Copy("$TARGET", "$SOURCE"))
      </file>
      <file name="file.in">file.in</file>
    </scons_example>

    <para>

    Notice that the action returned by the &Copy; factory
    will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings
    at the time &file_out; is built,
    and that the order of the arguments
    is the same as that of a builder itself--that is,
    target first, followed by source:

    </para>

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

    <para>

    You can, of course, name a file explicitly
    instead of using &cv-TARGET; or &cv-SOURCE;:

    </para>

    <scons_example name="factories_Copy2">
      <file name="SConstruct" printme="1">
Command("file.out", [], Copy("$TARGET", "file.in"))
      </file>
      <file name="file.in">file.in</file>
    </scons_example>

    <para>

    Which executes as:

    </para>

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

    <para>

    The usefulness of the &Copy; factory
    becomes more apparent when
    you use it in a list of actions
    passed to the &Command; builder.
    For example, suppose you needed to run a
    file through a utility that only modifies files in-place,
    and can't "pipe" input to output.
    One solution is to copy the source file
    to a temporary file name,
    run the utility,
    and then copy the modified temporary file to the target,
    which the &Copy; factory makes extremely easy:

    </para>

    <scons_example name="factories_Copy3">
      <file name="S" printme="1">
Command(
    "file.out",
    "file.in",
    action=[
        Copy("tempfile", "$SOURCE"),
        "modify tempfile",
        Copy("$TARGET", "tempfile"),
    ],
)
      </file>
      <!-- this is a non-displayed script that fiddles PATH to allow dummy "modify" command to work -->
      <file name="SConstruct">
def_env = DefaultEnvironment()
def_env.AppendENVPath('PATH', Dir('.'))
SConscript('S')
      </file>
      <file name="file.in">file.in</file>
      <file name="modify" chmod="0o755">
touch $*
      </file>
    </scons_example>

    <para>

    The output then looks like:

    </para>

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

    <para>
    The &Copy; factory has a third optional argument which controls
    how symlinks are copied.
    </para>

    <para>
    </para>

    <scons_example name="factories_SymlinkCopy">
      <file name ="SymlinkCopy" printme="1">
# Symbolic link shallow copied as a new symbolic link:
Command("LinkIn", "LinkOut", Copy("$TARGET", "$SOURCE", symlinks=True))

# Symbolic link target copied as a file or directory:
Command("LinkIn", "FileOrDirectoryOut", Copy("$TARGET", "$SOURCE", symlinks=False))
      </file>
    </scons_example>

  </section>

  <section id="sect-factories-delete">
  <title>Deleting Files or Directories:  The &Delete; Factory</title>

    <para>

    If you need to delete a file,
    then the &Delete; factory
    can be used in much the same way as
    the &Copy; factory.
    For example, if we want to make sure that
    the temporary file
    in our last example doesn't exist before
    we copy to it,
    we could add &Delete; to the beginning
    of the command list:

    </para>

    <scons_example name="factories_Delete1">
      <file name="S" printme="1">
Command(
    "file.out",
    "file.in",
    action=[
        Delete("tempfile"),
        Copy("tempfile", "$SOURCE"),
        "modify tempfile",
        Copy("$TARGET", "tempfile"),
    ],
)
      </file>
      <!-- this is a non-displayed script that fiddles PATH to allow dummy "modify" command to work -->
      <file name="SConstruct">
def_env = DefaultEnvironment()
def_env.AppendENVPath('PATH', Dir('.'))
SConscript('S')
      </file>
      <file name="file.in">file.in</file>
      <file name="modify" chmod="0o755">
touch $*
      </file>
    </scons_example>

    <para>

    Which then executes as follows:

    </para>

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

    <para>

    Of course, like all of these &Action; factories,
    the &Delete; factory also expands
    &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately.
    For example:

    </para>

    <scons_example name="factories_Delete2">
      <file name="SConstruct" printme="1">
Command(
    "file.out",
    "file.in",
    action=[
        Delete("$TARGET"),
        Copy("$TARGET", "$SOURCE"),
    ],
)
      </file>
      <file name="file.in">file.in</file>
    </scons_example>

    <para>

    Executes as:

    </para>

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

    <para>

    Note, however, that you typically don't need to
    call the &Delete; factory explicitly in this way;
    by default, &SCons; deletes its target(s)
    for you before executing any action.

    </para>

    <para>

    One word of caution about using the &Delete; factory:
    it has the same variable expansions available
    as any other factory, including the &cv-SOURCE; variable.
    Specifying <literal>Delete("$SOURCE")</literal>
    is not something you usually want to do!

    </para>

  </section>

  <section id="sect-factories-move">
  <title>Moving (Renaming) Files or Directories:  The &Move; Factory</title>

    <para>

    The &Move; factory
    allows you to rename a file or directory.
    For example, if we don't want to copy the temporary file,
    we could use:

    </para>

    <scons_example name="factories_Move">
      <file name="S" printme="1">
Command(
    "file.out",
    "file.in",
    action=[
        Copy("tempfile", "$SOURCE"),
        "modify tempfile",
        Move("$TARGET", "tempfile"),
    ],
)
      </file>
      <!-- this is a non-displayed script that fiddles PATH to allow dummy "modify" command to work -->
      <file name="SConstruct">
def_env = DefaultEnvironment()
def_env.AppendENVPath('PATH', Dir('.'))
SConscript('S')
      </file>
      <file name="file.in">file.in</file>
      <file name="modify" chmod="0o755">
touch $*
      </file>
    </scons_example>

    <para>

    Which would execute as:

    </para>

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

  </section>

  <section id="sect-factories-touch">
  <title>Updating the Modification Time of a File:  The &Touch; Factory</title>

    <para>

    If you just need to update the
    recorded modification time for a file,
    use the &Touch; factory:

    </para>

    <scons_example name="factories_Touch">
      <file name="S" printme="1">
Command(
    "file.out",
    "file.in",
    action=[
        Copy("$TARGET", "$SOURCE"),
        Touch("$TARGET"),
    ]
)
      </file>
      <file name="SConstruct">
SConscript('S')
      </file>
      <file name="file.in">file.in</file>
    </scons_example>

    <para>

    Which executes as:

    </para>

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

  </section>

  <section id="sect-factories-mkdir">
  <title>Creating a Directory:  The &Mkdir; Factory</title>

    <para>

    If you need to create a directory,
    use the &Mkdir; factory.
    For example, if we need to process
    a file in a temporary directory
    in which the processing tool
    will create other files that we don't care about,
    you could use:

    </para>

    <scons_example name="factories_Mkdir">
      <file name="S" printme="1">
Command(
    "file.out",
    "file.in",
    action=[
        Delete("tempdir"),
        Mkdir("tempdir"),
        Copy("tempdir/${SOURCE.file}", "$SOURCE"),
        "process tempdir",
        Move("$TARGET", "tempdir/output_file"),
        Delete("tempdir"),
    ],
)
      </file>
      <!-- this is a non-displayed script that fiddles PATH to allow dummy "process" command to work -->
      <file name="SConstruct">
def_env = DefaultEnvironment()
def_env.AppendENVPath('PATH', Dir('.'))
SConscript('S')
      </file>
      <file name="file.in">file.in</file>
      <file name="process" chmod="0o755">
touch $*
      </file>
    </scons_example>

    <para>

    Which executes as:

    </para>

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

  </section>

  <section id="sect-factories-chmod">
  <title>Changing File or Directory Permissions:  The &Chmod; Factory</title>

    <para>

    To change permissions on a file or directory,
    use the &Chmod; factory.
    The permission argument uses POSIX-style
    permission bits and should typically
    be expressed as an octal,
    not decimal, number:

    </para>

    <scons_example name="factories_Chmod">
      <file name="SConstruct" printme="1">
Command(
    "file.out",
    "file.in",
    action=[
        Copy("$TARGET", "$SOURCE"),
        Chmod("$TARGET", 0o755),
    ]
)
      </file>
      <file name="file.in">file.in</file>
    </scons_example>

    <para>

    Which executes:

    </para>

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

  </section>

  <section id="sect-execute-function">
  <title>Executing an action immediately:  the &Execute; Function</title>

    <para>

    We've been showing you how to use &Action; factories
    in the &Command; function.
    You can also execute an &Action; returned by a factory
    (or actually, any &Action;)
    at the time the &SConscript; file is read
    by using the &Execute; function.
    For example, if we need to make sure that
    a directory exists before we build any targets,

    </para>

    <scons_example name="factories_Execute">
      <file name="SConstruct" printme="1">
Execute(Mkdir('__ROOT__/tmp/my_temp_directory'))
      </file>
    </scons_example>

    <para>

    Notice that this will
    create the directory while
    the &SConscript; file is being read:

    </para>

    <scons_output example="factories_Execute" suffix="1">
      <scons_output_command>scons</scons_output_command>
    </scons_output>

    <para>

    If you're familiar with Python,
    you may wonder why you would want to use this
    instead of just calling the native Python
    <function>os.mkdir()</function> function.
    The advantage here is that the &Mkdir;
    action will behave appropriately if the user
    specifies the &SCons; <option>-n</option> or
    <option>-q</option> options--that is,
    it will print the action but not actually
    make the directory when <option>-n</option> is specified,
    or make the directory but not print the action
    when <option>-q</option> is specified.

    </para>

    <para>

    The &Execute; function returns the exit status
    or return value of the underlying action being executed.
    It will also print an error message if the action
    fails and returns a non-zero value.
    &SCons; will <emphasis>not</emphasis>, however,
    actually stop the build if the action fails.
    If you want the build to stop
    in response to a failure in an action called by &Execute;,
    you must do so by explicitly
    checking the return value
    and calling the &Exit; function
    (or a Python equivalent):

    </para>

    <sconstruct>
if Execute(Mkdir('__ROOT__/tmp/my_temp_directory')):
    # A problem occurred while making the temp directory.
    Exit(1)
    </sconstruct>

  </section>

</chapter>
