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.