# Platform-Independent File System Manipulation

&SCons; provides a number of platform-independent functions, called `factories`, that perform common file system manipulations like copying, moving or deleting files and directories, or making directories. These functions are `factories` 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.

## Copying Files or Directories: The &Copy; Factory

Suppose you want to arrange to make a copy of a file, and don't have a suitable pre-existing builder. 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. One way would be to use the &Copy; action factory in conjunction with the &Command; builder:

Command("file.out", "file.in", Copy("$TARGET", "$SOURCE"))
file.in

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:

scons -Q

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

Command("file.out", [], Copy("$TARGET", "file.in"))
file.in

Which executes as:

scons -Q

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:

Command( "file.out", "file.in", action=[ Copy("tempfile", "$SOURCE"), "modify tempfile", Copy("$TARGET", "tempfile"), ], )
def_env = DefaultEnvironment() def_env.AppendENVPath('PATH', Dir('.')) SConscript('S')
file.in
touch $*

The output then looks like:

scons -Q

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

# 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))

## Deleting Files or Directories: The &Delete; Factory

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:

Command( "file.out", "file.in", action=[ Delete("tempfile"), Copy("tempfile", "$SOURCE"), "modify tempfile", Copy("$TARGET", "tempfile"), ], )
def_env = DefaultEnvironment() def_env.AppendENVPath('PATH', Dir('.')) SConscript('S')
file.in
touch $*

Which then executes as follows:

scons -Q

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

Command( "file.out", "file.in", action=[ Delete("$TARGET"), Copy("$TARGET", "$SOURCE"), ], )
file.in

Executes as:

scons -Q

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.

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 `Delete("$SOURCE")` is not something you usually want to do!

## Moving (Renaming) Files or Directories: The &Move; Factory

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:

Command( "file.out", "file.in", action=[ Copy("tempfile", "$SOURCE"), "modify tempfile", Move("$TARGET", "tempfile"), ], )
def_env = DefaultEnvironment() def_env.AppendENVPath('PATH', Dir('.')) SConscript('S')
file.in
touch $*

Which would execute as:

scons -Q

## Updating the Modification Time of a File: The &Touch; Factory

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

Command( "file.out", "file.in", action=[ Copy("$TARGET", "$SOURCE"), Touch("$TARGET"), ] )
SConscript('S')
file.in

Which executes as:

scons -Q

## Creating a Directory: The &Mkdir; Factory

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:

Command( "file.out", "file.in", action=[ Delete("tempdir"), Mkdir("tempdir"), Copy("tempdir/${SOURCE.file}", "$SOURCE"), "process tempdir", Move("$TARGET", "tempdir/output_file"), Delete("tempdir"), ], )
def_env = DefaultEnvironment() def_env.AppendENVPath('PATH', Dir('.')) SConscript('S')
file.in
touch $*

Which executes as:

scons -Q

## Changing File or Directory Permissions: The &Chmod; Factory

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:

Command( "file.out", "file.in", action=[ Copy("$TARGET", "$SOURCE"), Chmod("$TARGET", 0o755), ] )
file.in

Which executes:

scons -Q

## Executing an action immediately: the &Execute; Function

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,

Execute(Mkdir('__ROOT__/tmp/my_temp_directory'))

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

scons

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

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 not, 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):

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