Coadjute-0.1.0: A generic build tool




Coadjute is a generic build tool, intended as an easier to use and more portable (in the sense that implementations don't differ) replacement for make. It's not tailored toward any particular language, and is not meant to replace tools which target a specific environment.

An example of simple usage:

 import Coadjute
 import System.Directory

 main = coadjute $ do
    rule' "Copy foo from src to obj"
          (\[s] t -> copyFile s t)
          (sourceDatum' (("obj"++) . drop 3) ["src/foo"])

By convention, this file should be called Adjutant.hs.

Compiled and run, it would copy src/foo to obj/foo whenever src/foo is older than obj/foo. With -d or --use-db passed, it would hash (currently MD5) src/foo, using that instead of modification time data to decide whether to run the copyFile or not.


Coadjute blocks

When using Coadjute, you give it all the information it needs within a monad inspiringly called Coadjute. Use coadjute to escape into the IO monad, letting Coadjute do all your hard work for you.

data Coadjute a Source

Coadjute is the main monad you'll be working in. You can use the rule family of functions to add rules whilst within this monad, and you can have a look at what arguments were given to you with getUserArgs.

For your convenience, a MonadIO instance is provided.

Defining rules

These are the primary functions available to you inside a Coadjute block: each one adds a single build rule, on the basis of which many build tasks may be constructed.

Each function takes:

  • A name for the rule. (Currently only used in error messages.)
  • A build action: this function will be run if the dependencies are deemed out of date compared to the target.
  • A list of dependencies pared with one or more targets.

For instance, you might have a rule "C files" which handles building of C code, thus:

 rule' "C files"
       (\_ c -> system ("gcc -c " ++ c))
       [(["foo.c"],"foo.o"), (["bar.c"],"bar.o")]

This example also demonstrates poor practices: you should really specify complete dependency data, such as header files.

The above example did not use command line arguments, so let's have a look at those. Let's say you want to build either a debug or a release version of your program, depending on a flag you pass in. Since this flag will affect all your object files, you want to tell Coadjute that you're interested in it:

 rule "C files" ["--debug"] ...

Now, if you've built your C files in release mode and want to switch to debug mode, Coadjute will know to rebuild even those files which you haven't changed, based simply on the fact that last time you did not pass --debug and this time you did.

You must still handle the flag's presence yourself using getUserArgs and acting on it: the [String] parameter is simply to tell Coadjute which flags that particular rule is affected by.

For now, only boolean flags are supported: either they're present or not.

Note that you must build with -d to store the argument data. You need specify this only the first time: as long as the database exists it will be used.

rule :: String -> [String] -> ([Source] -> Target -> IO ()) -> [([Source], Target)] -> Coadjute ()Source

A rule for building targets individually.

ruleM :: String -> [String] -> ([Source] -> [Target] -> IO ()) -> [([Source], [Target])] -> Coadjute ()Source

A rule for building multiple targets at once.

rule' and ruleM' are a pair of convenience functions for when you don't care about command line arguments.

rule' :: String -> ([Source] -> Target -> IO ()) -> [([Source], Target)] -> Coadjute ()Source

 rule' = flip rule []

ruleM' :: String -> ([Source] -> [Target] -> IO ()) -> [([Source], [Target])] -> Coadjute ()Source

 ruleM' = flip ruleM []

Other functions within Coadjute

getUserArgs :: Coadjute [String]Source

You should use this instead of System.Environment.getArgs, to let Coadjute handle its own command line arguments first.

Sources and Targets

Sources and targets are both paths to files or directories.