Flake is a build system that aims to replace Make. Flake is a small, portable, standalone executable and a general purpose build system. Like Make, Flake has a slight bias towards its own implementation language. It is distributed with build rules for C/C++ and Lua. In conjunction with Clang or GCC, it can generate a standalone executable with a single command-line invocation.
-
Flake does not invent its own programming language. It reuses Lua.
We chose Lua because it is simple, safe, portable and well-documented.
-
Automatic parallelization.
For example, consider the following build description.
return c.program { c.object 'a.c', c.object 'b.c', }
Flake recognizes that the program depends on 2 objects and that the objects are independent of each other. It therefore builds the objects in parallel. The C build rules have no special handling for this. Builders are only serialized with the output of one is the input of another.
-
Build steps can have dependencies on dependency generators.
return c.program(c.dependencies 'main.c')
-
Flake build scripts do not specify dependencies between files.
You will not see this:
%.o: %.c clang -c -o $@ $<
Flake dependencies are created implicitly. Builders return a handle to any file that needs to be created. Passing this handle to another function implies a dependency.
local obj = c.object 'a.c' return c.program(obj)
-
Names for intermediary files are automatically generated.
-
Flake uses lexical scoping to track dependencies. No global variables.
No globals means that you can share build libraries between teams without concern for naming conflicts.
-
Scales cleanly to multi-project builds.
local mylib = flake.importBuild '../mylib' return c.program { 'main.c', mylib.contents['mylib.a'] }
Flake builds subprojects in the context of the subproject's directory. Flake detects when relative paths are passed to/from the subproject and automatically adjusts the paths. In the example above, the subproject builds
./mylib.a
, but the top-level project sees../mylib/mylib.a
. No need forabspath()
noise.
$ make -C flake
$ export PATH=$PWD/flake/out/release:$PATH
$ cat hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
$ flake --silent c run hello.c
Hello, World!
$ cat hello.lua
print 'Hello, World!'
$ flake --silent lua run hello.lua
Hello, World!
$ cat build.lua
local system = require 'system'
local c = require 'c'
local lua = require 'lua'
return system.directory {
path = 'out',
contents = {
['hello-c'] = c.program{'hello.c'},
['hello-lua'] = lua.program{'hello.lua'},
},
}
$ flake build.lua
flake [OPTIONS]... [FILE [TARGET [TARGET_PARAMS]]]
Build the 'main' target from 'build.lua' in the current directory.
$ flake
Build the 'test' target from 'build.lua' in the current directory.
$ flake build.lua test
Build the 'main' target from 'foo.lua' in the current directory.
$ flake foo.lua
Build the 'main' target from 'build.lua. in the 'bar' directory.
$ flake -C bar
The Flake build system would not exist without the following contributions: