Features

  • Cross-platform: Windows is a first-class citizen - no dependency on a POSIX-compliant shell (or any shell). Werk files work on all platforms out of the box.

  • Simple language: Werk files are written in a simple and human-friendly language that's easy to understand. It is designed to be written by hand.

  • Task recipes: Real support for executing project scrips, similar to just recipes or .PHONY Make targets. A command recipe will be run at most once per werk invocation.

  • Build recipes: Files can be built from Make-like patterns, and rebuilt according to modification time.

  • Advanced outdatedness: werk does more than just compare file modification timestamps. Metadata is cached between runs to support additional sources of "outdatedness".

  • Separate output directory: All files produces by werk are put in a separate output directory, which is always what you want. This is hard to achieve with Make.

  • Globbing: Filesystem glob patterns work out of the box, and can be used reliably in dependencies. werk caches a hash of the glob result between builds, so file deletion is detected as a change. Globbing is based on the globset crate, which comes from ripgrep.

  • Paths can contain spaces: Make simply cannot deal.

  • Depfile support: Depfile output from C-like compilers such as clang, gcc, cl, glslc, etc. are specially supported in build recipes. When a recipe contains a depfile dependency, it is automatically built and included when evaluating the dependencies of that recipe.

  • .gitignore support: The ignore crate is used to hide files from werk.

  • Dry-run: Pass --dry-run to diagnose the build process without generating any output.

  • Concurrency: Build recipes and tasks run in parallel when possible.

  • (TODO) Autoclean: Werk is aware of which files it has generated, and can automatically clean them up from the output directory.

  • (TODO) Autowatch: Werk can be run in --watch mode, which waits for file changes and automatically rebuilds when any change is detected.

Limitations

  • Cross-platform: Paths and commands must work across all platforms. werk does not require a shell (like sh or bash), but that also means that common shell features are not available. The language provides cross-platform alternatives in most cases.

  • Declarative: Very advanced build logic can be difficult to express. If the limited expression support in werk is insufficient (and can't be easily supported in the model), consider using a more advanced solution like GNU make, CMake, ninja-build, scons, cargo-script, etc.

  • Separate output directory: It is not possible to put output files next to input files, and files in the output directory are assumed to be generated by werk.

  • Multiple recipes matching the same pattern: As opposed to Make, build recipes are not disambiguated by their inputs. This means that for example it is not possible to have two recipes that match %.o, where one takes %.c as input and the other takes %.cpp as input.

    • Workaround 1: Define separate recipes %.c.o and %.cpp.o to build the two kinds of source files with different output file extensions.
    • Workaround 2: Use capture patterns, so the build recipe pattern is %.(c|cpp), and use match expressions to generate the correct compiler arguments based on the captured pattern {0}, which will evaluate to either "c" or "cpp".
  • Multiple outputs from a single recipe: This is not supported. The typical example is a code generator that outputs both a header file and a source file, like bison. Note: This may be explicitly supported in the future.

    • Workaround: For the common case of a bison parser, define the recipe for the generated parser.h file, and add another recipe for the parser.c file that has no commands, but just depends on parser.h.
  • Detailed parallelism control: werk currently does not support marking specific recipes as "non-parallel". The only way to control parallelism is by supplying the --jobs command line parameter, which controls the number of worker threads.