HAREDO(1)

πŸ“¦
version 1.0.6 Β· 3 versions
, in alpine 3.24

πŸ’– HAREDO(1) πŸ’–

okay so bestie, you know how we're always opening the haredo(1) man page and going "huh?" β€” well, this is that page, but actually readable. it lives in the General Commands Manual section of the manual, and it's about haredo: a simple and unix-idiomatic build automator. no cap, that's literally the NAME line. slay. πŸŽ€


✨ SYNOPSIS ✨

haredo [-vqc] [-j <n>] targets... [++ sources...]

πŸ¦‹ DESCRIPTION πŸ¦‹

the whole vibe of haredo boils down to this, fr fr:

for each target argument:
     find and run its .do script
     if a .do script cannot be found, give a warning
return true if any argument is newer than their parent, else false.

Haredo's .do files are written in plain shell script, and will be run using sh -e so it exits on error. πŸ’…

🌸 Options 🌸

  • -v β€” print verbose logs, which means haredo will execute .do files with sh -ev rather than just sh -e πŸ‘€
  • -q β€” (quiet) don't print 'redo' logs 🀫
  • -c β€” (colorless) don't print 'redo' logs with ANSI escape codes 🎨
  • -f β€” force rebuild, meaning all files in the targets' dependency tree will be rebuilt πŸ”₯
  • -j <n> β€” run a maximum of <n> jobs in parallel ⚑

πŸ’œ Arguments πŸ’œ

haredo interprets each of its arguments as a target, up until the special argument ++, and then each argument after that is treated as a source file. and if you provide no arguments at all? haredo interprets this as if it had been called with the argument "all". iconic default behaviour tbh. 🫢

the difference between a target and a source is that haredo will attempt to build each target using its corresponding .do file, if it can be found, and if not it will emit a warning. in contrast, sources are assumed not to have a do file, and haredo will not try to find it. iykyk. ✨

finally, after running the .do script for each target, haredo checks all sources' and targets' "last-modified" timestamp in the filesystem, and returns a truthy status code if any of them are newer than the parent target. this is an indicator to the parent .do script that it should rebuild its target. main character status-check energy honestly. πŸ”₯

🧚 Finding a target's .do file 🧚

the process for finding a target's .do file is as follows, using the example target web/template.html.tmpl:

  • first check for the existence of web/template.html.tmpl.do.
  • if not found, starting in the target directory and then moving up to successive parent directories, check .do files in this order:
    • default.html.tmpl.do
    • default.tmpl.do
    • default.do

🌈 Running the .do file 🌈

haredo always runs a .do file from the directory it is in. the .do file is passed three arguments:

  1. the target path, relative to the .do file being run
  2. the basename path (target path without any file extensions)
  3. a temporary path where the target should be written to.

once the script is complete, the temporary path at $3 will be moved to the target path. this mechanism prevents half-written files sticking around if the script is interrupted. literally protective-icon behaviour. πŸ˜­πŸ’–

the following default values for environment variables are used. they can be overridden by setting their values before running haredo:

  • AR β€” ar
  • AS β€” as
  • CC β€” cc
  • CXX β€” c++
  • HARE β€” hare
  • HAREC β€” harec
  • LD β€” ld
  • QBE β€” qbe
  • SCDOC β€” scdoc

πŸ’… EXAMPLES πŸ’…

the typical structure of a .do script is as follows:

# assemble the dependencies
deps="dep1 dep2"

# run the build on dependencies, and check if they are newer
if haredo $deps
then
     # build the target
fi

the above script will only build the target if the result of running haredo on its dependencies gives a truthy result β€” which only happens when one of them is newer. obsessed with how clean that is. ✨

πŸ‘‘ A simple C project πŸ‘‘

here is a diagram of an example C program, rigged up with haredo .do files. the program (hello) simply prints the words "Hello, haredo!" to the terminal. note: this diagram can be parsed out into a directory using treecat(1) (https://git.sr.ht/~autumnull/treecat). πŸ¦‹

example/
β”œβ”€β”€ all.do:
β”‚ haredo hello || true
β”‚ ./hello
β”œβ”€β”€ build/
β”‚   β”œβ”€β”€ clean.do:
β”‚   β”‚ rm -f *.o
β”‚   β”œβ”€β”€ default.o.do:
β”‚   β”‚ src=../src/$2.c
β”‚   β”‚ if haredo ++ $src
β”‚   β”‚ then
β”‚   β”‚     $CC -o $3 -c $src
β”‚   β”” fi
β”œβ”€β”€ clean.do:
β”‚ haredo build/clean || true
β”‚ rm -f hello
β”œβ”€β”€ hello.do:
β”‚ deps="build/hello.o build/main.o"
β”‚ if haredo $deps
β”‚ then
β”‚     $CC -o $3 $deps
β”‚ fi
└── src/
    β”œβ”€β”€ hello.c:
    β”‚ #include <stdio.h>
    β”‚ #include "hello.h"
    β”‚
    β”‚ void hello() {
    β”‚     puts("Hello, haredo!");
    β”‚ }
    β”œβ”€β”€ hello.h:
    β”‚ void hello();
    β”œβ”€β”€ main.c:
    β”‚ #include "hello.h"
    β”‚
    β”‚ void main() {
    β”‚     hello();
    β”” }

the main entry point for haredo is the all.do file. it simply calls "haredo hello", ignoring whether it's been updated or not ("|| true"), and then runs the executable. πŸ’«

the primary .do script for "hello" is "hello.do", which exists, so haredo will execute it. in hello.do we see a more complex use of haredo. we define the dependencies, then we call haredo on them. this will run the build script for each .o file, and then return true if either of them is newer than "hello". if they are, then the "if" block is executed. inside the if block there is an environment variable $CC, which defaults to "cc", the system's c compiler. there is also the argument $3, which is a temporary file to which each .do file should write its target, and it will then be moved to the target location after the script is finished, to prevent broken builds. it's technically possible to write straight to the target path, but this is ill-advised. πŸ™ˆ (bestie. don't.)

now, each of the .o files referenced in hello.do don't have their own .do file, so they both default to using "build/default.o.do". in this script, the $2 argument is used (the basename of the target, i.e. without file extensions) to find the .c file that the object is built from. then in the call to haredo, the special argument '++' is used before the source file name to indicate that it doesn't have a .do script, since it's a source file. everything else in that file should be familiar from hello.do 🫢

and that's all! with this setup, the relevant .o file will be rebuilt, along with the executable, each time one of the source files changes. there is also a "clean.do", which can be run with "haredo clean" to clean up all the build files. ate. πŸ’…πŸ”₯


🌟 SEE ALSO 🌟

sh(1) β€” that's it, that's the whole list πŸ’€


πŸŽ€ AUTHORS πŸŽ€

maintained by Autumn! <autumnull@posteo.net>, with significant contributions from Ember Sawady <ecs@d2evs.net>. up-to-date sources can be found at https://sr.ht/~autumnull/haredo and bugs/patches can be submitted by email to ~autumnull/haredo-devel@lists.sr.ht πŸ’Œ


and that's the whole page, bestie β€” this version of HAREDO(1) was dated 2026-02-16, so go forth and build things, you absolute icon πŸ§šπŸ’œβœ¨

πŸ“¦ where this page comes from