| .gitignore | Adds graphviz output and improves cli handling | |
| bashbuilder | Fixes use of alias dependencies as command parameters and adds startup time logging | |
| README.md | Improves documentation | |
bashbuilder — bash-only project builder
Table of Contents
Getting Started
- Copy the script
bashbuilderto some place you like, preferably your project directory. For less typing, you may want to create an aliasbbto callbashbuilderfrom the current directory. - Write a build script,
bb.buildby default, which contains calls to thetargetcommand to describe your build targets, how they depend on each other and how to build them. - Run
./bashbuilder targetto build the given target according to the recipe described in your build script.
Functional Overview
A target is something you want to build. Usually it is a file or a the
contents of a whole directory. To build target1 it may be necessary to build
targetA and targetB first. Then those two are called dependencies of
target1.
Of course a dependency can have its own dependencies too and different targets may have the same dependencies. The result is a directed graph without cycles. If a target directly or indirectly has itself as a dependency, this is flagged as an error.
To create a target, it has an associated command. It is anything
the bash knows as a command, function, script or program to call. If
bashbuilder determines that it must (re)create a target target1 with its
command cmd1 it will call the command with 1+N arguments, namely the target
and its dependencies, like
cmd1 target1 targetA targetB
To know whether a target needs to be updated, a target has a state which,
internally, is a checksum (sha256sum for now) of the target's content,
combined with the check-sums of its dependencies. A target gets updated, if any
of the following two conditions hold:
- After updating the dependencies their combined state is different from a previously recorded state.
- The state of the target is different from a previously recorded state.
The latter makes sure that manual editing or even deletion of the target can be detected.
If there is no previous recorded state, then the target is outdated by definition and must be updated.
If a dependency is updated but its resulting state is the same as before, like after it was accidentally deleted, the target will not be build unnecessarily.
A minimal example:
## Declare an initial target, assume it is an existing file in the project
target README.md
## Declare README.html as file to be created by running runPandoc
target README.html state=file cmd=runPandoc \
README.md
function runPandoc() {
## params are generally: target dep1 dep2 ...
pandoc "1ドル" >"2ドル"
}
Command Line
bashbuilder [f=buildscript] [ll=loglevel] [info=dump|list|graph] target ...
Without named parameters, the given targets will be updated in the order given
and without resetting state in between. This means that if t2 depends on t1
and when running
bashbuilder t2 t1
the t1 is redundant, because it will be updated as a dependency of t2
anyway.
- Parameter
fdefines the build script to use and defaults tobb.build. - Parameter
lldefines the global log level. It can be changed also from commands in the build script by callingsetLogLevel. - Parameter
inforequests information about the defined build:listprints a list of targets, preferring their aliasdumpprints alltargetcommandsgraphprints the dependency graph in graphviz format
API
target
target target [alias=name] [state=state] [cmd=command] [dependency ...]
The target command registers the given target. All other parameters are
optional under conditions described below. The given string target can be used
on the bashbuilder command line to bring this target up-to-date.
If no state parameter is provided, the target must specify an existing file
or directory, in which case the value for state is file or dir
respectively. Example:
target src/main/java
registers the directory src/main/java as a target. Its state is dir which
means that the state of the target is the checksum of all files recursively
found in this directory. Since it is a source directory, it does not need a
command to build it and neither has a dependency list.
The alias parameter can be used to give the target a nice name, mainly for use
on the bashbuilder command line. But the alias may also be used in a
dependency list. We could write
target build/classes alias=compile state=dir cmd=compileJava \
src/main/java
and later run the compilation with bashbuilder compile rather than with
bashbuilder build/classes. The alias may also be used in a dependency list.
The state parameter's value must be a command with one parameter, which will be the the target. It must produce a string which represents the condensed state of the target, typically a checksum like SHA-256. In addition the following shortcuts can be used:
diras a shortcut forbbDirContentStatefileforbbFileContentStatenewforbbNewStatewhich produces a new state each time it is calledfixedforbbFixedStatewhich always produces the same state
The cmd parameter specifies which command to call if the target is found to
be outdated. If the target has a dependency, a cmd must be specified, though
it may of course be true or : (the bash no-op) command. The provided
command will be called with the target as 1ドル and all un-aliased
dependencies as further parameters. Example:
target src/main/java alias=javaSrc
target build/classes state=dir cmd=compileJava \
javaSrc
This registers build/classes as a target. We have to provide the state
parameter since in an initial build the directory does not exist, and
bashbuilder cannot guess the state generator. The command to update the target
is compileJava which will be called as
compileJava build/classes src/main/java
if the target must be updated. Note how the target alias javaSrc used as
dependency is replaced by the original target src/main/java.
setLogLevel
setLogLevel level
The setLogLevel command controls the verbosity of the build process. The
level must be one of
ERRORonly shows build process failuresINFOis the default and shows which targets are updated and with which commandCMDalso shows the output of commands run to update targets which is normally suppressedDEBUGshows some additional output compared toCMD
log
log level text ...
The level must be one of the words listed under
setLogLevel. If it has equal or higher priority than the level
set with setLogLevel, the message is echoed to the standard error output.
bbFileContentState
bbFileContentState target
Runs the content of the target file through bbSum. If the file
does not exist, bbNewState is called instead.
bbDirContentState
bbDirContentState target
Runs the content of all files recursively in
directory target through bbSum. If the directory does not exist,
bbNewState is called instead.
bbFileListContentState
The function reads file names, one per line (using bash's read -r command),
and sends their content through bbSum. The final checksum is
printed on stdout. This function can be used to implement custom state
generator functions, for example like
function javaFilesState() {
find "1ドル" -type -f -name "*.java" | bbFileListContentState
}
bbNewState
bbNewState target
Independent of the provided target a state string is returned which is never
the same as a previously returned state. (Except if you manage to run
bashbuilder twice within the same second.)
bbSum
bbSum
The function converts stdin to a checksum printed on stdout, currently using
sha256sum.
bbToGraphviz
bbToGraphviz
The function produces the current dependency graph on stdout in
graphviz format. Instead of requesting the same output
with command line argument info=graph, you could use the following targets to
keep a file dependencies.svg up to date with regard to your build script
bb.build:
target bb.build # so that we can use it as a dependency
target dependencies.svg state=file alias=graph cmd=createGraph \
bb.build
function createGraph() {
bbToGraphviz | dot -Grankdir=LR -Tsvg >"$target"
}
This assumes you have the graphviz installed, which comes with the dot program.
To update the dependency graph, you would run ./bashbuilder graph or just
bb graph if you have defined bb to be the suggested shortcut to run your
copy of bashbuilder.