6.3. Build Item Name Scoping

In this section, we discuss build item name scoping rules. Build item name scoping is one mechanism that can be used to restrict which build items may directly depend on which other build items.

Build item names consist of period-separated segments. The period separator in a build item's name is a namespace scope delimiter that is used to determine which build items may directly refer to which other build items in their Abuild.conf files. It is a useful mechanism for allowing a build item to hide the fact that it is composed of lower-level build items by blocking others from accessing those lower-level items directly.

Each build item belongs to a namespace scope equal to the name of the build item after removing the last period and everything following it. For example, the build item “A.B.C.D” is in the scope called “A.B.C”. We would consider “A.B” and “A” to be ancestor scopes. The build item name itself also defines a scope. In this case, the scope “A.B.C.D” would contain “A.B.C.D.E”. Any build item name scope that starts with “A.B.C.D.” (including the period) would be a descendant scope to “A.B.C.D”. Any build item whose name does not contain a period is considered to belong to the global scope and is accessible by all build items.

One build item is allowed to access another build item by name if the referenced build item belongs to the accessing build item's scope or any of its ancestor scopes. Figure 6.1, “Build Item Scopes”, shows a number of build items arranged by scope. In this figure, each build item defines a scope whose members appear in a gray box at the end of a semicircular arrowhead originating from the defining build item. Each build item in this figure can see the build items that are direct members of the scope that it defines, the build items that are siblings to it in its own scope, and the build items inside of any of its ancestor scopes. You may wish to study the figure while you follow along with the text below.

Figure 6.1. Build Item Scopes

Build Item Scopes

Build items are shown here grouped by scope. Each build item is connected to the scope that it defines.

To illustrate, we will consider item A1.B1.C1. The build item A1.B1.C1 can access the following items for the following reasons:

It cannot access these items:

The item A1.B1.C1 can be accessed by the following items:

It cannot be accessed by these items:

To give a more concrete example, suppose you have a globally accessible build item called networking that was internally divided into private build items networking.src and networking.test. A separate build item called logger would be permitted to declare a dependency on networking but not on networking.src or networking.test. Assuming that it did not create any circular dependencies, networking.test would also be allowed to depend on logger.

Note that these restrictions apply only to explicitly declared dependencies. It is common practice to implement a “public” build item as multiple “private” build items. The public build item itself would not have an Abuild.interface file, but would instead depend on whichever of its own private build items contain interfaces it wants to export. It would, in fact, be a pass-through build item. Because dependencies are inherited, items that depend on the public build item will see the interfaces of those private build items even though they would not be able to depend on them directly. In this way, the public build item becomes a facade for the private build items that actually do the work. For example, the build item networking would most likely not have its own Abuild.interface or Abuild.mk files. Instead, it might depend on networking.src which would have those files. It would probably not depend on networking.test since networking.test doesn't have to be built in order to use networking. [16] This means that it would be okay for networking.test to depend on networking since doing so would not create any circular dependencies. Then, any build items that depend on networking indirectly depend on networking.src and would see networking.src's Abuild.interface.

There is nothing that a build item can do to allow itself to declare a direct dependency on another build item that is hidden within another scope: the only way to gain direct access to a build item is to be its ancestor or to be a descendant of its parent. (There are no restrictions on indirect access.) There are times, however, when it is desirable for a build item to allow itself to be seen by build items who would ordinarily not have access to it. This is accomplished by using the visible-to key in Abuild.conf. We defer discussion of this feature until later; see Chapter 25, Build Item Visibility.

[16] Although networking doesn't have to depend on networking.test, you might be tempted to put the dependency in so that when you run the check target for all dependencies of networking, you would get the test suite implemented in networking.test. Rather than using a dependency for this purpose, you can use a trait instead. For information about traits, see Section 9.5, “Traits”. A specific example of using traits for this purpose appears in that section.