Tooltree Build

Tooltree is a project structured as a set of packages that exist as siblings immediately under the tooltree directory. The goal is to structure a project — a versioned collection of assets, such a git repository — as a set of smaller components on which development can proceed independently.

Dependencies between packages are minimized and managed. Packages import selected packages and they export a directory that will be provided to other packages when they import it. This allows a package to change its internal organization substantially while still supporting the same contracts with other packages.

Each package has a Makefile that describes how it is built. Here are some build command examples that work across Tooltree packages. The following commands deal with assets within the current package:

make           Build/update the `default` target.
make clean     Delete build products.
make graph     Show dependences between assets built by `make`.

The following commands deal with assets outside of the current package:

make imports           Build/update all imported packages
make deep              Build/update imported packages *and* this package.
make clean imports     Delete build products for imported packages.
make 'Graph(imports)'  Show imported packages and their relationships.

The top-level Makefile is itself an empty package that includes other packages for the convenience of validating the entire project.

Package Makefiles

See any of tooltree/*/Makefile for examples. Here are the key elements of a tooltree Makefile:

Package Descriptions

Packages are defined with the following variables:

Tooltree.mk will compute the following for each package imported by the package currently being built:

External Projects

To leverage tooltree, an external project's Makefile at a minimum will declare its own package name (with thisPackage = NAME), declare the packages it imports (with package.NAME.imports = ...), define includeImports if it uses imported makfiles, and finally include tooltree.mk at the bottom.

For example:

Alias(default).in = LuaExe(prog.lua)

thisPackage = root
package.root.imports = build-lua
includeImports = build-lua/build-lua.mk
include <PATH-TO-TOOLTREE>/build/tooltree.mk

Note: While thisPackage will default to the name of the directory that contains it, this default should probably be avoided in the case of top-level makefiles because that directory name may vary.

If the project is itself structured as multiple packages, it can provide a make include file that can be used by all its packages, analogously to how tooltree.mk is used within Tooltree. It would describe all the packages in its project and then include tooltree.mk.

For example, the project-level include file might look like this:

# projectX.mk

# this can be included from Makefiles in other directories...
Xdir := $(dir $(lastword $(MAKEFILE_LIST)))

package.foo.dir := $(Xdir)foo
package.foo.outdir = $(VOUTDIR)/exports
package.foo.imports = build-lua

package.bar.dir := $(Xdir)bar/
package.bar.outdir = .
package.bar.imports = foo

include $(Xdir)/<PATH_TO_TOOLTREE>/tooltree.mk

Its own package makefiles would resemble those in tooltree:

# projectX/foo/Makefile

Alias(default).in = Ship(exports)
exports = LuaExe(foo)

includeImports = build-lua/build-lua.mk
include ../projectX.mk

Tooltree Changes

This new build directory comes along with a rewrite of the build system that was motivated by the following goals:

In more detail, the changes comprise the following: