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 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:

A1.B1.C1.D1 because it belongs to the
scope that A1.B1.C1 defines:
A1.B1.C1

A1.B1.C2 because it is in the same
scope as A1.B1.C1:
A1.B1

A1.B1 and A1.B2
because they belong to an ancestor scope:
A1

A1 and Q because
they are global

It cannot access these items:

A1.B1.C1.D1.E1 because it is hidden in
scope A1.B1.C1.D1

A1.B1.C2.D1 because it is hidden in
scope A1.B1.C2

Q.R because it is hidden in scope
Q

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

A1.B1 because it is its parent

A1.B1.C2 because it is its sibling

A1.B1.C1.D1 and
A1.B1.C1.D1.E1 because they are its
descendants

A1.B1.C2.D1 because it can see
A1.B1.C1 as a member of its ancestor
scope A1.B1

It cannot be accessed by these items:

A1.B2, A1,
Q, and Q.R, none
of which can see inside of A1.B1

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.