Redo implementation in Bourne Shell

I have implemented the build system redo as designed by DJB in Bourne Shell. To understand how redo can be simpler, more flexible, more powerful and more reliable than make, read Introduction to redo and/or redo: a top-down software build system.

The implementation itself depends only on GNU Core Utilities or BusyBox. It takes around twice the time of apenwarr's Python redo implementation to return from a dependency check while using fewer resources.

redo
  the main program
redo-always
  marks the current target as always needing to be rebuilt
redo-dot
  prints redo dependency graph in DOT format (example output, example rendering)
redo-ifchange
  adds dependencies for the current target (if a dependency changes, the target will be rebuilt)
redo-ifcreate
  adds non-existence dependencies for the current target (if a non-existence dependency is created, the target will be rebuilt)
redo-ood
  prints a list of all target files that are out of date
redo-targets
  prints a list of all target files that exist (a target is a file redo can build)
redo-sources
  prints a list of all source files that exist (a source is a dependency that is not a target)
redo-stamp
  detects if the current target has changed (see apenwarr's documentation)

# Frequently Asked Questions

# How do I build a file with redo?

To build a file `target`, you have to create a dofile `target.do` with shell commands that write the intended result of the build either to standard output or to its parameter $3. Then run `redo target`.

The default target is `all`: `redo` without arguments executes commands from `all.do`.

# What do the redo parameters $1, $2, $3 mean?

When redo runs a dofile, it gives it three parameters:

$1 | filename of target
$2 | basename of target, without extension if default dofile is used
$3 | filename of temporary output file that is renamed on build success

# How does the extension removal in parameter $2 work?

Redo removes the extension that it gets from the default dofile filename:

Target | Dofile | Parameter $2
--- | --- | ---
`a.b.c` | `a.b.c.do` | a.b.c
`a.b.c` | `default.do` | a.b.c
`a.b.c` | `default.c.do` | a.b
`a.b.c` | `default.b.c.do` | a

# How do I declare dependencies with redo?

Use the redo-ifchange command in a dofile: `redo-ifchange dependency` inside `target.do` means: If the `target` is built, the `dependency` is built if it does not exist and recorded as a dependency. On subsequent builds, if `dependency` does not exist or has changed since the last build, both `dependency` and `target` are rebuilt.

# How does this redo implementation check dependencies?

For dependency checking, this implementation of redo checks the dependencies' ctime against the stored ctime. If the ctime differs, it checks the dependencies' md5sum against the stored md5sum. This is arguably more useful than just using ctime.

# How do I declare non-existence dependencies with redo?

Use the redo-ifcreate command in a dofile: `redo-ifcreate ne_dependency` inside `target.do` means: If `target` is built, the non-existing file `ne_dependency` is recorded as a non-existence dependency. If `ne_dependency` exists on subsequent builds, `target` is rebuilt.

# What are command line options for this redo implementation?

Short option | Long option | Effect
--- | --- | ---
-d | --debug | print dependency checks as they happen
-h | --help | print usage instructions and exit
-s | --shuffle | randomize build order to find dependency bugs
-x | --xtrace | print commands as they are executed (variables expanded)

# How can I use this redo implementation to build in parallel?

You can use the shell builtins `&` and `wait` to execute commands asynchronously. The following line in a dofile builds targets a and b in parallel: `redo-ifchange a & redo-ifchange b & ; wait`

#