'Make' compatibility

The syntax for Buildsome's definition files is similar tho that of make, however only a small subset is implemented, and a few constructs are added. The differences are described here.

Directory structure

Unlike make, Buildsome is designed for a directory structures from the get-go. Therefore, when executing in any directory, it looks up the directory structure to find the top-most Buildsome.mk file. It starts the evaluation process from there.

Purity

The evalution of Buildsome's definition files, which is needed for the process of devising a target tree, is done in a precise way that is completely independent of the state of other files in the directory structure. It is therefore not possible to generate these definition files during the execution of Buildsome itself, and there is no make like process of "Makefile-reloading". This limitiation is intentional.

Macros

Macro assignment

There are currently two ways to define a macro, shown in the next example:

FOO = value
BAR ?= value

In this case variable FOO will be assigned with the new string always, and BAR will be assigned only if it was not assigned before.

Command line build customization

There is a special case concerning variables whose names start with FLAG_: If FLAG_X is defined with with ?=, then the user can pass --with X in the command line when executing Buildsome in order to set an initial value enabled, or --without to set it to disabled.

Macros are unrelated to POSIX environment

Unlike in make there is no relation between macros in Buildsome definition files and POSIX environment variables. In fact, when target are executing their commands, it is done with a very minimal environment variable set.

The POSIX environment is made pristine for target execution. For example, except $PATH and $HOME, any environment variable set in .bashrc, will not appear set under target execution. The reason is to ensure reproducability of the build between users, each having their own possibly environment-sensitive tools under a different execution environment.

Scope saving

Using local { and local }, it is possible to save-restore the values of all variables.

A = 2
local {
A = 5
local }

In the example above, A will revert to the value 2 after the local } line.

Substitution

Macros can refer to other macros via ${...}. The expansion itself however takes place only in the target definition.

CFLAGS=-O2 ${CFLAGS_FEATURE}

Cartesian expansion

Similarly to expansion done in some shells, comma-delimited expressions enclosed in curly braces are expanded. For example, the expression x{a,b,c} is expanded to xa xb xc.

Multiline assignments

Using \, it is possible to extend a macro assignment to multiple lines. For example:

DEFAULT_CFLAGS_C_COMMON=      \
    ${DEFAULT_CFLAGS_COMMON}  \
    ${CFLAGS_POISON_FULLY}    \

Include directives

It is possible to recursively include other files. For example:

include otherdir/otherfile.mk

Conditional evaluation

Currently, make-style ifeq and ifneq are supported, along with else.

CLFAGS_FEATURE=
ifeq ($(flag_BAR),enabled)
CLFAGS_FEATURE=-DFEATURE
endif

There is a special case with variable whose names start with FLAG_. If those are defined with ?=, then the user can pass --with in the command when executing buildsome in order to set an initial value enabled.

Target definition

Similarly to make, the syntax for targets is as follows:

<outputs> : <optionally-specified-inputs> (| <order-only-inputs>)
<tab char><script line 1>
<tab char><script line 2>
<tab char><script line n>

Not that unlike in make, all script lines are executed as one shell script, instead of separately.

The target named default is built if no target is mentioned in the command line.

Macro expansion takes place at the target line specification, and in it associated shell script.

Inputs can be specified in order to assist in parallel first builds. However, if the execution of the target does make use of these inputs, a warning is emitted.

Simple patterns

Target patterns can be specified similarly to the make syntax. One or more files can be outputs, and % serves as a wild card. For example:

(note that in the cpp to o rule below, nothing more needs to be specified, as all other input dependencies such as included headers are automatically detected.)

%.o: %.cpp
        ${COMPILEXX}

However, there is an important difference between the mae and Buildsome functionality of patterns. In the example above, the pattern does not match targets in a subtree (e.g .subdir/a.o), but only in the project's root directory. Though it is possible to reuse patterns, albeit in an explicit manner–a topic we shall visit in the next section.

Reuse of patterns in sub-directories

In order to make a pattern apply in more than one directory, we can use combine Scope saving, Include directives, and variable assignments in the following manner.

build/patterns.mk:

${curdir}/%.o: ${curdir}/%.cpp
        ${COMPILEXX}

Buildsome.mk:

curdir=.
include patterns/include.mk
include subdir/include.mk

subdir/include.mk:

local {

curdir=subdir
include patterns/include.mk

local }

Note the assignment of the Buildsome variable curdir, that serves as the path to the current directory. It is more common to use the valid variable name . instead of the lengthy curdir, so that $. can be used inplace of ${curdir}.

Wildcard patterns

Wildcard patterns allow a one-to-many target instanitation, by adding a wildcard in the output side of a pattern target.

For example:

local_%.sep.*.c: %.foo
        ${TEMPLATE_MAKER}

The rule above will match for files names which match the globbing pattern local_*.sep.*.c. For example, with the file local_test.sep.bar.c, the target will expand as local_test.sep.bar.c: bar.foo.

Special macros

The following macros have special meaning when expansion takes place inside target definitions.

  • $@ - expands to the first output of the target.
  • $< - expands to the first specified input of the target.
  • $^ - expands to all specified inputs of the target (not including order-only inputs).
  • $| - expands to all order-only inputs of the target.

In addition, the following two modifiers can be appended, e.g. $@(D):

  • (D) - take only the directory name.
  • (F) - take only the base name.

Phony targets

Similarly to make, phony targets can be specified, using the pseudo .PHONY output target. These are targets that don't actually generate files, but serve only as 'always fall-through' nodes in the dependency graph, to link groups of targets together.

.PHONY: default

Order-only inputs

Some target inputs can be specified in the target definition line in a way that does not affect the expansion of $^.

For example:

output: input | order-only
        cat $|
        cat $^
        echo $^ > $@

In the target above, the execution read from order-only, but only the string input will be written to output.