浏览代码

First half of the ackbuilder documentation.

David Given 7 年之前
父节点
当前提交
ea4142daee
共有 1 个文件被更改,包括 199 次插入0 次删除
  1. 199 0
      first/ackbuilder.md

+ 199 - 0
first/ackbuilder.md

@@ -0,0 +1,199 @@
+ackbuilder
+==========
+
+# ackbuilder
+
+## What is it?
+
+ackbuilder is a very small build tool inspired by [bazel](https://bazel.io/)
+which uses either make or [ninja](https://ninja-build.org/) as a backend.
+
+It supports fully parallelisable builds (in both make and ninja), as well as
+hopefully-robust support for rules which generate more than one output file,
+which is something make is very, very bad at.
+
+It was written because the ACK is a really horribly complex thing to build and
+there wasn't anything else.
+
+## Basic concepts
+
+Complete example, using the built-in C rules. This should be saved in a file
+called `build.lua`:
+
+    cprogram {
+      name = 'prog',
+      srcs = { "./*.c" },
+    }
+
+This defines a rule `prog` which, when built, compiles all the source files in
+the same directory as the `build.lua` file into an executable.
+
+Slightly more complex example:
+
+    clibrary {
+      name = "library",
+      srcs = { "./library.c" },
+      hdrs = { "./library.h" },
+    }
+
+    cprogram {
+      name = 'prog2',
+      srcs = { "./prog2.c" },
+      deps = { "+library" }
+    }
+
+If we move the library into another directory, we can invoke it like this:
+
+    cprogram {
+      name = 'prog3',
+      srcs = { "./prog3.c" },
+      deps = { "path/to/library+library" }
+    }
+
+* Targets starting with `./` are relative to **the current directory** (i.e.
+  the one the build file is in).
+
+* Targets starting with a path are relative to the top directory of the
+  project.
+
+* Targets containing a `+` refer to a named target in another build file. So,
+  on encountering the library in `prog3` above, ackbuilder will look for
+  `path/to/library/build.lua`, load it, and then try to find a target in it
+  called `library`.
+
+**Warning**: files are interpreted from top to bottom; every time a target
+referring to another build file is seen for the first time, that file is
+interpreted then and there. You can't have circular dependencies (these are
+caught and an error is generated). You can't refer to a target defined below
+you in the same source file (these are not caught, and just won't be found).
+
+The `cprogram` and `clibrary` rules, by the way, are sophisticated enough to
+automatically handle library and header paths. The exported headers by the
+library are automatically imported into the program.
+
+## `simplerule` and `normalrule`
+
+These are the building blocks out of which all other rules are made. If you
+want to run your own programs, you will be using these.
+
+`simplerule` is the simplest. You give it inputs, and outputs, and commands,
+and it does it.
+
+    simplerule {
+	  name = 'sorted-input',
+	  ins = { './input.txt' },
+	  outs = { './output.txt' },
+	  commands = {
+	  	"sort < %{ins} > %{outs}"
+	  }
+	}
+
+In a command block, `%{...}` will evaluate the Lua expression between the
+braces; various useful things are in scope, including the list of inputs and
+outputs.
+
+However, this ends up leaving the output file lying around in the project
+directory, which we don't want, so we usually use `normalrule` instead.
+(`normalrule` is not strictly part of the ackbuilder core; it's in the standard
+library along with `cprogram` and `clibrary`.)
+
+    normalrule {
+	  name = 'sorted-input',
+	  ins = { './input.txt' },
+	  outleaves = { 'output.txt' },
+	  commands = {
+	    "sort < %{ins} > %{outs}"
+	  }
+	}
+
+Note `outleaves`; there is no `./`. This is a list of leaf filenames. The rule
+will create a directory in the object tree and put the files specified in it,
+somewhere; you don't care where. You can refer to the output file via the
+target name, so:
+
+    normalrule {
+	  name = 'reversed',
+	  ins = { '+sorted-input' },
+	  outleaves = { 'reversed.txt' },
+	  commands = {
+	    "rev < %{ins} > %{outs}"
+      }
+    }
+
+One common use for this is to generate C header or source files.
+
+    normalrule {
+	  name = 'reversed_h',
+	  ins = { '+reversed' },
+	  outleaves = { 'reversed.h' },
+	  commands = {
+	    'xxd -i %{ins} > %{outs}'
+      }
+	}
+
+	cprogram {
+	  name = 'prog',
+	  srcs = { './*.c' },
+	  deps = { '+reversed_h' }
+	}
+
+Now you can refer to `reversed.h` in one of your C files and it'll just work
+(`+reversed_h`'s output directory gets added to the include path
+automatically).
+
+## Defining your own rules
+
+Like this:
+
+  definerule("sort",
+    {
+	  srcs = { type="targets" },
+	},
+	function(e)
+	  return normalrule {
+	    name = e.name,
+		ins = e.srcs,
+		outleaves = { 'sorted.txt' },
+		commands = {
+		  "sort < %{ins} > %{outs}"
+		}
+      }
+	}
+  )
+
+  sort {
+    name = 'sorted',
+	srcs = { './input.txt' }
+  }
+
+You give `definerule()` the name of the rule you want to define, a description
+of the properties the rule will take, and a callback that does the work.
+
+You can do anything you like in the callback, including defining as many
+targets as you like; but remember that all targets must have unique names, so
+for any temporary files you probably want something like `name =
+e.name.."/intermediate"` to ensure uniqueness.
+
+The callback should end by returning an invocation of another rule, with `name
+= e.name` as above.
+
+Rules are defined whenever a build file containing them is seen. Letting this
+happen automatically doesn't always work so you probably want to explicitly
+include it:
+
+    include("foo/bar/baz/build.lua")
+
+Rule properties are typed and can be specified to be required or optional (or have a default value).
+
+    definerule("sort",
+	  {
+	     srcs = { type="targets" },
+		 numeric = { type="boolean", default=false }
+	  }
+	  ...omitted...
+
+The most common one is `targets`. When the rule is invoked, ackbuilder will
+resolve these for you so that when your callback fires, the property is a
+flattened list of target objects.
+
+