diff --git a/first/ackbuilder.md b/first/ackbuilder.md index 1b211a49e..0a09a8aaa 100644 --- a/first/ackbuilder.md +++ b/first/ackbuilder.md @@ -1,6 +1,3 @@ -ackbuilder -========== - # ackbuilder ## What is it? @@ -13,7 +10,12 @@ 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. +there wasn't anything else. ackbuilder is pretty rough and ready but it does +sort of work. Be prepared for bugs. + +This document is a very rapid brain dump of how the build file works. It +doesn't cover actually running the tool (because that bit's pretty nasty) --- +go look at the top level Makefile to see that for now. ## Basic concepts @@ -67,6 +69,10 @@ 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). +Build files each get their own private global scope. If you randomly set a +variable, it won't be seen by other build files. (Use `vars` for that; see +below.) Build files are only loaded once. + 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. @@ -183,17 +189,161 @@ 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). +Rule properties are typed and can be specified to be required or optional (or +have a default value). If you try to invoke a rule with a property which isn't +declared, or missing a property which should be declared, you'll get an error. definerule("sort", { srcs = { type="targets" }, - numeric = { type="boolean", default=false } + numeric = { type="boolean", optional=true, 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. +(The `optional=true` part can be omitted if you specify a default which isn't +`nil`.) +Types include: + + * `targets`: the most common one. 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. + + * `strings`: a Lua table of strings. If the invoker supplies a single string + which isn't a table, it'll get wrapped in one. + + * `string`: a string. + + * `boolean`: a boolean (either `true` or `false`; nothing else is allowed). + + * `table`: a Lua table. + + * `object`: any Lua value. + +## Target objects + +When a rule callback is run, any targets it needs will be resolved into target +objects. These are Lua objects with assorted useful stuff in them. + + * `object.is`: contains a set telling you which rules made the object. e.g. + `object.is.cprogram` is true if `object` was built with `cprogram`. Bear in + mind that `object.is.normalrule` is _also_ going to be true. + + * `object.dir`: the object's build directory. Only exists if the object was + built with `normalrule`. + +There are other properties (`fullname` and `outs`). Please don't use these; use +`targetnamesof()` and `filenamesof()` as described below. + +## The standard library + +Your build files are supplied a pile of useful functions. + +### Manipulating target lists + +A target list is a possibly nested set of tables containing either target +objects or strings. All of these functions will implicitly flatten the list and +resolve any strings into target objects before doing anything else to them. +Most of these functions can be supplied with varargs parameters. + +e.g. `targetsof(a, b)` is equivalent to `targetsof({a, b})` is equivalent to +`targetsof({a, {b}})`. + + * `targetsof(...)`: just flattens the list and resolves any string target + names. + + * `filenamesof(...)`: returns a list of output files for all the supplied + targets. + + * `targetnamesof(...)`: returns a list of fully qualified target names for + all the supplied stargets. + + * `selectof(targets, pattern)`: returns only those targets whose outputs + contain at least one file matching the pattern. + +### Manipulating filename lists + +Like the target list functions, all of these implicitly flatten any nested +tables. They all return lists; however, as a special exception, if any of the +functions which take varargs parameters have a single parameter which is a +string, they return just a string. + +e.g. `abspath({f})` returns a table; `abspath(f)` returns a string. + + * `abspath(...)`: attempts to return the absolute path of its arguments. This + isn't always possible due to variable references. + + * `basename(...)`: returns the basenames of its arguments (the file part of + the path). + + * `dirname(...)`: returns the directory name of its arguments. + + * `matching(files, pattern)`: returns only those files which match a Lua + pattern. + + * `replace(files, pattern, repl)`: performs a Lua pattern replace on the list + of files. + + * `uniquify(...)`: removes duplicates. + +### Other things + + * `include(file)`: loads another build file, if it hasn't been loaded before. + +## Variables + +There are two types of variable, mostly for hysterical reasons. + +### Makefile variables + +(Despite the name, these work on ninja too.) + +Filenames can contain variable references of the form `$(FOO)`. These are +expanded at build time based on definitions supplied on the ackbuilder command +line. + +ackbuilder assumes that these are absolute paths and won't attempt to +manipulate them much. + +I want to get rid of these at some point. + +### ackbuilder variables + +These are expanded by ackbuilder itself. + +Every rule invocation contains a magic property, `vars`. When a rule's commands +are executed, the variables provided in the template expansion are calculated +by combining all `vars` settings in the call stack (including the top level +build file). + +Easiest to explain with an example: + + cprogram { + name = 'another_test', + srcs = { './*.c' }, + vars = { + cflags = { '-g', '-O3' } + } + } + +When `cprogram` builds each C file, the command will refer to `%{cflags}`. The +value above will be flattened into a space-separated string and substituted in. + +Setting a variable this way will _override_ any definition further up the call +stack. However, you can do this: + + vars.cflags = { '-g' } + + cprogram { + name = 'another_test', + srcs = { './*.c' }, + vars = { + ["+cflags"] = { '-O3' } + } + } + +Now `cflags` will default to `-g` everywhere, because it's set at the top +level; but when `another_test` is built, it'll be `-g -O3`. + +ackbuilder variables are only expanded in command templates, not in filenames.