- Zig 83.6%
- Nix 9%
- C 4.7%
- Python 1.5%
- Makefile 1.2%
make-analyze
make-analyze records the dependency graph that GNU Make computes for a
Makefile. It is both a command-line tool, macli, and a Zig library,
make_analyze, for programs that need to inspect Make-based build graphs
without reimplementing Makefile evaluation.
The project is aimed at C and C-adjacent codebases that still use Make, but where other tooling needs a structured view of the build graph.
Manual
Build from source
The project builds with Zig 0.16:
zig build
zig-out/bin/macli --help
The examples below assume macli is on PATH; otherwise use
zig-out/bin/macli.
For development, the repository also provides a Nix flake with the pinned Zig toolchain and test dependencies:
nix develop
zig build test
Run without cloning
If you have Nix installed, run the current main branch as a flake directly
from Codeberg:
nix run 'https://codeberg.org/Sh4pe/make-analyze/archive/main.tar.gz#default' -- --help
Record a graph
macli record evaluates a Makefile and writes the discovered graph as JSON.
macli record -m Makefile -o graph.json
Useful options:
-m, --makefile <path>selects the Makefile to analyze. This option is required.-o, --output <path>writes JSON to a new file. Without it, JSON is printed to stdout.-t, --target <name>analyzes a specific target instead of the default target.--expand-recipesstores recipes after Make variable expansion.
Convert a graph
macli convert converts a recorded JSON graph to another graph format. The
format is selected with a subcommand.
To produce Graphviz DOT:
macli convert dot graph.json > graph.dot
dot -Tsvg graph.dot -o graph.svg
To produce a graph-tool .gt file:
macli convert gt graph.json > graph.gt
Useful options:
macli convert dot --show-node-idsincludes internal node IDs in DOT labels, which is useful when debugging graph merges across recursive Make invocations.
Internal recursive make mode
macli make is an implementation detail used by macli record when it follows
recursive Make recipes. It is not intended to be invoked manually.
Testing and reading .gt files
The graph-tool .gt integration check has its own Python environment. To debug
it manually, enter the check derivation shell:
nix develop .#checks.x86_64-linux.gt
The pytest suite in that shell expects MA_GT_FIXTURES to point at a directory
containing paired JSON and .gt outputs from the macli checks.
How It Works
make-analyze vendors the GNU Make 4.4 C sources and builds them as a static
library. Instead of parsing Makefiles independently, the Zig code initializes GNU
Make, lets it read and evaluate the Makefile, and then uses GNU Make's own goal,
target, and dependency data to traverse the build graph.
That matters because real Makefiles often depend on GNU Make semantics: implicit
rules, phony targets, variable expansion, included files, target-specific state,
and recursive invocations. Reusing GNU Make keeps analysis close to what make
itself would do.
Recursive Make is handled by detecting recipes that GNU Make marks as recursive.
When such a recipe is selected for execution, make-analyze forks a child
process for that recipe by replacing $(MAKE) with the internal macli make
command. The child process analyzes the nested Makefile and sends its graph back
to the parent over IPC, where the subgraph is merged into the main graph.
This is intentionally faithful, but it has an important consequence: recursive recipes are executed so their subgraphs can be discovered. Any side effects in those recipes can happen during analysis.
Why?
There are already useful tools around Make graph visualization and debugging, including makefile2graph, remake, and makeppgraph. Those tools are valuable when the goal is to visualize or debug a Make build directly.
make-analyze exists for a different integration point: projects where C code is
still built by Make, but higher-level orchestration is done by the Zig build
system or another Zig program. In that setting, the build graph is data the host
tool wants to consume, not only a picture to inspect.
For complicated Make builds, make-analyze provides the graph structure that a
Zig integration can query and transform.
The CLI is the easiest way to record and render graphs, but the core output of
this repository is also the make_analyze Zig module. It exposes the graph data
structure and analysis entrypoints so other Zig projects can use Make-derived
dependency information directly.
Contributing
make-analyze is still incomplete and likely has many bugs, especially on real
Makefiles that rely on less common GNU Make behavior. One of the most useful
ways to help is to run it against real projects and report bugs here when
analysis fails, produces surprising output, or misses part of the build graph.