December 2023
Makesure is a task/command runner that
I am developing. It is somewhat similar to the well-known make
tool, but
without most of its idiosyncrasies (and with a couple of unique features!).
Just is a very popular alternative command runner that positions itself as “a handy way to save and run project-specific commands”.
This article compares the two tools on some particular real-world examples.
Makesure was deliberately designed to not have its own programming language. In essence, it consists of goals + dependencies + handful of directives + shell. So it doesn’t have two types of variables and therefore has an idiomatic solution:
@lib
GIT_COMMIT="$(git rev-parse --short HEAD)"
GIT_TIME="$(git show -s --format=%ci $GIT_COMMIT | tr -d '\n')"
@goal default
@use_lib
echo "$GIT_COMMIT"
echo "$GIT_TIME"
Makesure doesn’t echo script lines by default. Command runner (as opposed to a build tool, like make
) is recipe-oriented, not line-oriented. The unit of work is a recipe, not a line. So the recipe body is just an implementation detail.
However, it logs the goal names being executed, such that it’s clear what’s going on:
$ ./makesure
goal 'fhtagn_installed' [already satisfied].
goal 'debug' ...
GNU Awk 5.0.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.2.0)
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
goal 'debug' took 0.006 s
goal 'prepared4tests' [empty].
goal 'tests.basic.tush' ...
TESTS PASSED : tests.basic.tush
goal 'tests.basic.tush' took 0.273 s
goal '*.tush' [empty].
goal 'tested' [empty].
goal 'default' [empty].
total time 0.281 s
Though, if you need, Makesure also has -x
option with the same function as in shell (activates command tracing).
Makesure doesn’t have such issue because it uses a special directive @doc
for a goal description, which doesn’t interfere with regular comments:
# some regular comment
@goal do_it
# some other comment
@doc 'This is a very useful goal'
echo 'Doing...'
Overall Makesure chose an approach with a uniform syntax via directives rather than an ad-hoc syntax for every feature. This proved to be very solid choice for many reasons:
Makesurefile
’s syntax is a valid shell syntax.Makesure doesn’t need installation
!include
By default, just
invokes the first recipe. Makesure by default invokes the goal named default
. So, although makesure doesn’t have includes, if it had, the issue would not happen.
just
can fail to execute shebang-recipes due to ‘Permission denied’Makesure doesn’t produce temp files during goals execution, so it’s not susceptible to this problem.
Makesure uses shell (instead of its own programming language) and relies on shell variables (instead of its own kind of variables).
The idiomatic solution to the described problem using parameterized goals:
@define BUILD_DIR 'build'
@define FILE_NAME 'out'
@goal pandoc @params ARG EXT @private
echo pandoc input.md -o "$BUILD_DIR/$ARG/$FILE_NAME.$EXT"
@goal html @params ARG
@depends_on pandoc @args ARG 'html'
@goal pdf @params ARG
@depends_on pandoc @args ARG 'pdf'
@goal foo
@depends_on html @args 'foo'
@depends_on pdf @args 'foo'
Calling:
$ ./makesure -l
Available goals:
foo
html@foo
pdf@foo
$ ./makesure foo
goal 'pandoc@foo@html' ...
pandoc input.md -o build/foo/out.html
goal 'html@foo' [empty].
goal 'pandoc@foo@pdf' ...
pandoc input.md -o build/foo/out.pdf
goal 'pdf@foo' [empty].
goal 'foo' [empty].
$ ./makesure html@foo
goal 'pandoc@foo@html' ...
pandoc input.md -o build/foo/out.html
goal 'html@foo' [empty].
Such support could help avoiding unnecessary re-runs.
Idempotence is achievable elegantly with Makesure using @reached_if directive.
It’s clear that as an author of the tool I’m completely biased.
But to me the minimalism and inherent simplicity and coherence allows Makesure to eliminate having some problems, as well as solve many problems more elegantly.
If you are interested, I invite you to try out the makesure utility in your projects.