Example program

This example shows a simple skam protocol for packaging up the contents of a directory using tar and gzip. Fire up your favourite text editor and enter the following lines into a file util.skm

Writing a skamfile

compress(File)
  req: File
  srun: gzip -c File
  flat: File.gz

tar(Dir)
  req: Dir
  srun: tar cf target Dir
  flat: Dir.tar
	    
This protocol has two targets, compress and tar. Each target has one argument; arguments are specified using a leading upper-case character.

Each target has a set of tags. The req tag specifies the requirements for building the target. In this case the requirement is just the subject of the target. The run or srun tag specifies how to construct a command string to be executed by the operating system shell. We are using srun here, which means that skam will take care of the output and error streams of the script. The flat tag specifies where the target will be built on the filesystem.

You can use this protocol to package up a directory, ./MyDir; presuming such a directory exists, you can do this:

skam -f util.skm "compress(tar('MyDir'))"
            
Note that 'MyDir' must be enclosed in single quotes; any token with a leading upper-case character must be quoted if it is not to be treated as a variable. The program output will look like this:
cjm$ skam -f util.skm "compress(tar('MyDir'))"
[util.P dynamically loaded, cpu time used: 0.0090 seconds]
top_level_target(compress(tar(MyDir)))
init_make_target(target = compress(tar(MyDir)),n_subtargets = 1,flat = MyDir.tar.gz)
    init_make_target(target = tar(MyDir),n_subtargets = 1,flat = MyDir.tar)
        is_terminal(MyDir)
        exec_code
             ( tar cf MyDir.tar MyDir 1>/tmp/MyDir.tar 2>/tmp/MyDir.tar.stderr ) 
            (mode sync)
            return_status = OK
    term_make_target(target = tar(MyDir) * OK)
    exec_code
         ( gzip -c MyDir.tar 1>/tmp/MyDir.tar.gz 2>/tmp/MyDir.tar.gz.stderr ) 
        (mode sync)
        return_status = OK
term_make_target(target = compress(tar(MyDir)) * OK)


***********
goal: compress(tar(MyDir))
final_status = OK
***********
            
skam output is indented to reflect the hierarchical nature of the build process; MyDir must be tarred before it is compressed. When skam commences building a target, it will generate output init_make_target. Any subtargets are then made before executing any necessary commands.

You can see the exact commands with the

Dependency Management

skam takes care of dependency management for you; if you try to build the target a second time:

cjm$ skam -f util.skm "compress(tar('MyDir'))"
[util.P dynamically loaded, cpu time used: 0.0000 seconds]
top_level_target(compress(tar(MyDir)))
init_make_target(target = compress(tar(MyDir)),n_subtargets = 1,flat = MyDir.tar.gz)
    init_make_target(target = tar(MyDir),n_subtargets = 1,flat = MyDir.tar)
        is_terminal(MyDir)
    term_make_target(target = tar(MyDir) * OK)
term_make_target(target = compress(tar(MyDir)) * OK)


***********
goal: compress(tar(MyDir))
final_status = OK
***********
            
In order to determine if a target needs to be rebuilt, skam uses status targets. You can see these on the filesystem. The default mapping produces a file suffixed ".OK" for every target that was built correctly.
cjm$ ls -F
MyDir/
MyDir.tar
MyDir.tar.OK
MyDir.tar.gz
MyDir.tar.gz.OK
MyDir.tar.gz.stderr
MyDir.tar.stderr
            
As well as building the compressed tar file, and the intermediate tar file, there are two status targets and two standard error output targets (these are produced automatically when the srun tag is used).

When determining if a target needs to be rebuilt, skam will compare the filesystem timestamps of both the target and subtarget's OK status flags. If the subtarget is more recent than the target, this forces a target rebuild.

Note that there is no OK status target for MyDir. This is because MyDir is a terminal target. There is no rule for building it. Skam expects terminal targets to be present. In this case, skam will use the timestamp of the target itself rather than the timestamp of the status flag for determining whether a rebuild is required.

Experiment with recreating the main tgz target; remove the intermediate tar file and run skam again; try adding something to MyDir and rebuilding.

Extending the example

We can introduce an intermediate 'convenience' target "tgz" which depends on the completion of a nested target. We will also add a new zero-argument target "all" which hardcodes a set of targets to be built.

tgz(Dir)
  req: compress(tar(Dir))

all
  req: tgz('MyDir') tgz('MyDir2')
            
We can now build the targets "tgz('MyDir')" or "all" on the command line (the "all" target presumes you also have a directory MyDir2). You can also omit the target name from the command line altogether, and the "all" target will be made.

Target sets

If you wish to maintain a large collection of directories to be tgzed, see the chapter on looping. Here is a brief summary

<relation name="dir_to_distribute">
MyDir1
MyDir2
MyDir3
MyDir4
MyDirA
MyDirB
MyDirC
</relation>


all
  req-l: L {setof(tgz(X),dir_to_distribute(X),L)}