Build tool for C and C++ that derives build graphs/dependencies
  • OCaml 87.2%
  • Shell 12.8%
Find a file
2026-09-07 00:54:55 +00:00
assets feat(tools): record a live watch session 2026-09-07 00:54:55 +00:00
bin fix(watch): drop the digest memo between rounds 2026-09-07 00:49:48 +00:00
examples feat(run): run blocks for commands the build does not produce 2026-09-06 16:54:52 +00:00
lib fix(watch): drop the digest memo between rounds 2026-09-07 00:49:48 +00:00
tools feat(tools): record a live watch session 2026-09-07 00:54:55 +00:00
.gitignore build: add dune project skeleton and library stanza 2025-12-06 19:22:41 +00:00
dune-project build: add dune project skeleton and library stanza 2025-12-06 19:22:41 +00:00
README.md feat(rule): chain rules and depend on the tool they run 2026-09-06 19:38:29 +00:00

Build tool for C and C++ that derives the build graph and dependencies automatically, without needing Makefiles or generated build files

meowc

meowc tracks dependencies at the action level and only reruns actions whose inputs have changed. Outputs are compared by content so downstream actions are skipped when recompilation produces byte identical object files

Adding a declaration to a header that most of the project includes fans out to fourteen translation units across four targets, and nothing relinks, because a declaration emits no code and the objects come back byte identical

editing a header

dune build && ln -sf _build/default/bin/meowc.exe meowc
cd examples/raytracer && ../../meowc build && ../../meowc test

A run block names a command and the targets it needs, which is how a project drives something meowc does not build itself. meowc run demo builds those targets and hands over, appending any arguments you pass after the name; with no use it builds everything, as a bare meowc build does

run demo {
  use     help
  command guile --no-auto-compile main.scm
}

A rule runs because something needs the file it writes, a run block runs because you asked for it, so a command that produces nothing has somewhere to live

A rule can use the tool it runs, whether that is a target meowc builds or a script sitting in the tree. Either way the tool becomes an input to the rule, so it is built before the rule runs and editing it regenerates what it wrote. Rules are read in written order, so a later rule can glob what an earlier one produces and a pipeline can be spelled out in stages

bin tablegen {
  srcs tablegen.c
}

rule tables {
  inputs  data/*.txt
  use     tablegen
  outputs gen/${stem}.c
  command ${builddir}/bin/tablegen ${in} ${out}
}

meowc graph --show renders the graph to SVG and opens it in the browser, which is the one viewer everyone has that draws vectors rather than pixels. It uses $MEOWC_VIEWER, then $BROWSER, then whichever of firefox or xdg-open is installed. On a terminal --dot does the same, since raw graphviz on a screen is for nobody, while a pipe or a redirect still writes the source

Tools are taken from the triple prefix when a matching toolchain is installed, otherwise the host compiler is invoked with --target. Each triple gets its own build directory and probe cache, so host and cross trees do not invalidate each other, and the triple drives platform, arch, format and the conditionals built on them

meowc --target x86_64-w64-mingw32 build
meowc --target aarch64-linux-gnu --sysroot /opt/sysroots/aarch64 build

Output names and link flags follow the object format rather than the operating system. ELF gets -fPIC, -shared and -Wl,-soname, MachO gets -dynamiclib and -Wl,-install_name, and COFF drops both the position independent code flag and the soname, writing rt.dll next to the librt.dll.a that dependents link against

execve weighs the arguments and the environment together against ARG_MAX, so past a few thousand objects a link or archive step stops being able to start at all. Commands meowc composes itself spill into a response file when they approach that limit and run as @file, while the graph keeps the command it meant, so a flag that only the response file ever sees still invalidates the action. A rule runs an arbitrary program, which need not understand @file, and is left alone

TODO

  • Integrate precompiled headers into dependency tracking and invalidation
  • Replace periodic mtime polling in watch with filesystem event notifications such as inotify