1
1
Fork
You've already forked bashbuilder
0
forked from harald/bashbuilder
Build system written and specified in bash
  • Shell 100%
2025年04月13日 11:55:04 +02:00
.gitignore Adds graphviz output and improves cli handling 2025年04月12日 20:13:25 +02:00
bashbuilder Fixes use of alias dependencies as command parameters and adds startup time logging 2025年04月13日 09:45:26 +02:00
README.md Improves documentation 2025年04月13日 11:55:04 +02:00

bashbuilder — bash-only project builder

Table of Contents

Getting Started

  1. Copy the script bashbuilder to some place you like, preferably your project directory. For less typing, you may want to create an alias bb to call bashbuilder from the current directory.
  2. Write a build script, bb.build by default, which contains calls to the target command to describe your build targets, how they depend on each other and how to build them.
  3. Run ./bashbuilder target to 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:

  1. After updating the dependencies their combined state is different from a previously recorded state.
  2. 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 f defines the build script to use and defaults to bb.build.
  • Parameter ll defines the global log level. It can be changed also from commands in the build script by calling setLogLevel.
  • Parameter info requests information about the defined build:
    • list prints a list of targets, preferring their alias
    • dump prints all target commands
    • graph prints 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:

  • dir as a shortcut for bbDirContentState
  • file for bbFileContentState
  • new for bbNewState which produces a new state each time it is called
  • fixed for bbFixedState which 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

  • ERROR only shows build process failures
  • INFO is the default and shows which targets are updated and with which command
  • CMD also shows the output of commands run to update targets which is normally suppressed
  • DEBUG shows some additional output compared to CMD

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.