Skip to content
/ walk Public

A fast, general purpose, graph based build and task execution utility.


Notifications You must be signed in to change notification settings


Repository files navigation

Build Status Go Report Card Latest Version

walk is a fast, general purpose, graph based build and task execution utility.

Heavily inspired by make and redo.


  • Fast parallel execution.
  • Graph based dependency management.
  • Maximum composability with existing UNIX tooling.
  • Describe targets and their dependencies as simple executables.
  • Universal execution; execute walk from any directory.


Using Go 1.7+:

$ go get -u

Or grab the latest release from


walk is built on top of a very simple concept; when you want to build a target, walk executes a file called Walkfile to determine:

  1. What other targets the given target depends on.
  2. How to build the target.

For example, if you wanted to build a program called prog from main.c and parse.c, you might write a Walkfile like this:


# The first argument is the "phase", which will either be `deps` or `exec`. In
# the `deps` phase, the Walkfile should print the name of the targets that this
# target depends on.

# The second argument is the name of the target, like `prog`, `parse.o`, etc.

case $target in
    case $phase in
      # Prog depends on the object files we'll build from source. We simply
      # print each dependency on a single line.
        echo main.o
        echo parse.o
      exec) exec gcc -Wall -o $target $($0 deps $target) ;;
    esac ;;

  # A generic recipe for building a .o file from a corresponding .c file.
    case $phase in
      deps) echo ${target//.o/.c} ;;
      exec) exec gcc -Wall -o $target -c $($0 deps $target) ;;
    esac ;;

  # When invoking walk(1) without any arguments, it defaults to a target called
  # `all`.
    case $phase in
      deps) echo prog ;;
    esac ;;

  # In general, it's good practice to include a fallback rule like this, in
  # case someone tries to build a target that we don't know how to build (or
  # someone makes a typo).
  *.c|*.h) ;; # static files
  *) >&2 echo "No rule for target \"$target\"" && exit 1 ;;

When you execute walk all, the following happens internally:

  1. walk resolves all of the dependencies, and builds a graph:

    $ Walkfile deps all
    $ Walkfile deps prog
    $ Walkfile deps parse.o
    $ Walkfile deps main.o
    $ Walkfile deps parse.c
    $ Walkfile deps main.c
  2. walk executes all of the targets, starting with dependencies:

    $ Walkfile exec parse.c
    $ Walkfile exec main.c
    $ Walkfile exec main.o
    $ Walkfile exec parse.o
    $ Walkfile exec prog
    $ Walkfile exec all

Ultimately, all of our targets end up getting invoked, and prog is built:

$ walk
ok	main.c
ok	parse.c
ok	parse.o
ok	main.o
ok	prog
ok	all

We can print the dependency graph to verify that our dependency chain is what we expect:

$ walk -p dot
digraph {
  "(root)" -> "all"
  "all" -> "prog"
  "prog" -> "main.o"
  "prog" -> "parse.o"
  "parse.o" -> "parse.c"
  "main.o" -> "main.c"

And that's it. Wait, that's it? That's it. walk is quite simply, just syntactic sugar over executing a binary as a graph.

See also man walk.