当构建一个目标X
,它依赖于先构建另一个目标Y
(例如,一个必须链接到X的库),Y
被称为X
的依赖项,而X
被称为Y
的依赖目标。
为了了解目标依赖关系,让我们继续上面的例子,看看top/app/Jamfile
如何使用来自top/util/foo
的库。如果top/util/foo/Jamfile
包含
lib bar : bar.cpp ;
那么,要在top/app/Jamfile
中使用此库,我们可以这样写:
exe app : app.cpp ../util/foo//bar ;
虽然app.cpp
引用的是一个普通的源文件,但../util/foo//bar
指的是另一个目标:一个在../util/foo
的Jamfile中声明的库bar
。
一些其他的构建系统有特殊的语法来列出依赖库,例如LIBS
变量。在Boost.Build中,你只需要将库添加到源代码列表中。
假设我们使用以下命令构建app
:
b2 app optimization=full define=USE_ASM
哪些属性将用于构建foo
?答案是某些特性会被传播——Boost.Build尝试使用具有相同传播特性值的依赖项。<optimization>
特性会被传播,因此app
和foo
都将使用完全优化进行编译。但<define>
不会被传播:它的值将按原样添加到a.cpp
的编译器标志中,但不会影响foo
。
让我们进一步改进这个项目。该库可能有一些头文件,在编译app.cpp
时必须使用它们。我们可以手动将必要的#include
路径添加到app
的需求中,作为<include>
特性的值,但这项工作将重复用于所有使用foo
的程序。更好的解决方案是修改util/foo/Jamfile
,如下所示:
project : usage-requirements <include>. ; lib foo : foo.cpp ;
使用需求不是应用于正在声明的目标,而是应用于其依赖目标。在本例中,<include>.
将应用于所有直接依赖于foo
的目标。
另一个改进是使用符号标识符来引用库,而不是Jamfile
位置。在一个大型项目中,一个库可以被许多目标使用,如果它们都使用Jamfile
位置,那么目录组织的更改将需要大量的工作。解决方案是使用项目ID——与目录布局无关的符号名称。首先,我们需要通过将以下代码添加到Jamroot
来分配项目ID:
use-project /library-example/foo : util/foo ;
其次,我们修改app/Jamfile
以使用项目ID:
exe app : app.cpp /library-example/foo//bar ;
语法/library-example/foo//bar
用于引用ID为 /library-example/foo
的项目中的目标bar
。我们已经实现了我们的目标——如果库被移动到不同的目录,只需要修改Jamroot
。请注意,项目ID是全局的——不允许两个Jamfile将相同的项目ID分配给不同的目录。
如果希望某个项目中的所有应用程序都链接到某个特定的库,可以通过使用<library>
属性来避免必须在每个目标的源代码中直接指定它。例如,如果/boost/filesystem//fs
应该链接到项目中的所有应用程序,则可以将<library>/boost/filesystem//fs
添加到项目的依赖项中,如下所示:
project : requirements <library>/boost/filesystem//fs ;