6.4. Simple Build Tree Example

Now that the topic of build items and build trees has been explored in somewhat more depth, let's take a look at a simple but complete build tree. The build tree in doc/example/general/reference/common illustrates many of the concepts described above.

The first file to look at is the Abuild.conf belonging to this tree's root build item:


tree-name: common
child-dirs: lib1 lib2 lib3
supported-traits: tester

This is a root build item configuration file, as you can see by the presence of the tree-name key. Notice that it lacks a name key, as is often the case with the root build item. This Abuild.conf contains the names of some child directories and also a build tree attribute: supported-traits, which lists the traits that are allowed in the build tree. We will return to the topic of traits in Section 9.5, “Traits”. In the mean time, we will direct our focus to the child build items.

The first child of the root build item of this tree is in the lib1 directory. We examine its Abuild.conf:


name: common-lib1
child-dirs: src test
deps: common-lib1.src

This build item is called common-lib1. Notice that the name of the build item is not the same as the name of the directory, but it is based on the name of the directory. This is a typical strategy for naming build items. Abuild doesn't care how you name build items as long as they conform to the syntactic restrictions and are unique within a build tree. Coming up with a naming structure that parallels your system's architecture is a good way to help ensure that you do not create conflicting build item names. However, you should avoid creating build item names that slavishly follow your directory structure since doing so will make it needlessly difficult for you to move things around. A major feature of abuild is that nothing cares where a build item is located, so don't set a trap for yourself in which you have to rename a build item when you move it!

This build item does not have any build or interface files. It is a pass-through build item. It declares a single dependency: common-lib1.src, and two child directories: src and test.

Next, look at the common-lib1.src build item's Abuild.conf in the common/lib1/src directory:


name: common-lib1.src
platform-types: native

The first thing to notice is this build item's name. It contains a period and is therefore private to the common-lib1 scope. That means that it is not accessible to build items whose names are not also under that scope. In particular, a build item called common-lib2 would not be able to depend directly on common-lib1.src. It would instead depend on common-lib1 and would inherit the dependency on common-lib1.src indirectly.

This build item doesn't list any child directories and, as such, is a leaf in the file system hierarchy. It also happens not to declare any dependencies, so it is also a leaf in the dependency tree, though one does not imply the other. This build item configuration file contains the platform-types key, as is required for all build items that contain build or interface files. In addition to the Abuild.conf file, we have an Abuild.mk file and an Abuild.interface file:


TARGETS_lib := common-lib1
SRCS_lib_common-lib1 := CommonLib1.cpp
RULES := ccxx


INCLUDES = ../include
LIBS = common-lib1

There is nothing in these files that is fundamentally different from the basic C++ library example shown in Section 3.4, “Building a C++ Library”. We can observe, however, that the INCLUDES variable in Abuild.interface actually points to ../include rather than the current directory. This simply illustrates that abuild doesn't impose any restrictions on how you might want to lay out your build items, though it is recommended that you pick a consistent way and stick with it for any given build tree. You should also avoid paths that point into other build items. Instead, depend on the other item and put the variable there. As a rule, if you ever have two interface variables or assignments that resolve to the same path, you are probably doing something wrong: a significant feature of abuild is that allows you to encapsulate the location of any given thing in only one place. Instead, figure out who owns a given file or directory and export it from that build item's interface. We will not study the source and header files in this example here, but you are encouraged to go to the doc/example/general/reference/common directory in your abuild source tree or installation directory to study the files further on your own.

Next, look at the test directory. Here is its Abuild.conf:


name: common-lib1.test
platform-types: native
deps: common-lib1
traits: tester -item=common-lib1.src

Notice that it declares a dependency on common-lib1. Since its name is also private to the common-lib1 scope, it would have been okay for it to declare a dependency directly on common-lib1.src. Declaring its dependency on common-lib1 instead means that this test code is guaranteed to see the same interfaces as would be seen by any outside user of common-lib1. This may be appropriate in some cases and not in others, but it demonstrates that it is okay for a build item that is inside of a particular namespace scope to depend on its parent in the namespace hierarchy. This build item also declares a trait, but we will revisit this when we discuss traits later in the document (see Section 9.5, “Traits”).

In addition to the lib1 directory, we also have lib2 and lib3. These are set up analogously to lib1, so we will not inspect every file. We will draw your attention to one file in particular: observe that the common-lib2.src build item in reference/common/lib2/src declares a dependency on common-lib3:


name: common-lib2.src
platform-types: native
deps: common-lib3

We will return to this build tree later to study build sets, traits, and examples of various ways to run builds.