![]() |
Home | Libraries | People | FAQ | More |
This section will guide you though the most basic features of Boost.Build
V2. We will start with the 「Hello, world」 example, learn how
to use libraries, and finish with testing and installing features.
本節將教給你一些 Boost.Build
V2 中最基本的特性。我們將從 「Hello, world」 例子開始,學習如何使用庫,並以測試和安裝特性結束。
The simplest project that Boost.Build can construct is stored in
example/hello/ directory. The project is described by
a file called Jamroot that contains:
Boost.Build 可以構建的最簡單工程保存在
example/hello/ 目錄下。該工程由一個名為 Jamroot 的文件來描述,它包含:
exe hello : hello.cpp ;
Even with this simple setup, you can do some interesting things. First of
all, just invoking bjam will build the hello
executable by compiling and linking hello.cpp
. By default, debug variant is built. Now, to build the release
variant of hello, invoke
即使只是這麼簡單的設置,你也可以做一些有趣的事情。首先,只需調用 bjam 就可以通過編譯和鏈接 hello.cpp 來構建 hello 的可執行文件。缺省情況是構建調試版本。要構 hello 的發佈版本,請調用:
bjam release
Note that debug and release variants are created in different directories,
so you can switch between variants or even build multiple variants at
once, without any unnecessary recompilation. Let us extend the example by
adding another line to our project's Jamroot:
注意,調試版本和發佈版本是在不同目錄下構建的,所以你可以在兩個版本間切換,或者一次構建多個版本,而不需要任何多餘的重編譯。讓我們來擴展這個例子,增加一行到我們的工程的 Jamroot 中:
exe hello2 : hello.cpp ;
Now let us build both the debug and release variants of our project again:
現在讓我們再次構建這個工程的調試和發佈版本:
bjam debug release
Note that two variants of hello2 are linked. Since we
have already built both variants of hello, hello.cpp
will not be recompiled; instead the existing object files will just be
linked into the corresponding variants of hello2. Now
let us remove all the built products:
注意,hello2 的兩個版本都被鏈接了。因為我們已經構建過 hello 的兩個版本,所以 hello.cpp
無須重編譯;而只須將已有的目標文件鏈接到 hello2 的相應版本中。現在我們來刪掉所有已構建的成品:
bjam --clean debug release
It is also possible to build or clean specific targets. The following two
commands, respectively, build or clean only the debug version of
hello2.
也可以只構建或清除指定的目標。以下兩個命令分別構建和清除
hello2 的調試版本。
bjam hello2
bjam --clean hello2
To portably represent aspects of target configuration such as
debug and release variants, or single- and multi-threaded
builds, Boost.Build uses features with
associated values. For
example, the debug-symbols feature can have a value of on or
off. A property is just a (feature,
value) pair. When a user initiates a build, Boost.Build
automatically translates the requested properties into appropriate
command-line flags for invoking toolset components like compilers
and linkers.
為了可移植地表示目標配置的情況,如調試版本和發佈版本、或者單線程和多線程,Boost.Build 使用了 特性features,它帶有關聯的 值values。例如,特性 debug-symbols 可以具有 on 或
off 的值。屬性property 是一對 (特性,值)。當用戶初始化一個構建時,Boost.Build
自動地將給定的屬性翻譯為適合的命令行選項,以調用象編譯器和鏈接器這些工具集組件。
There are many built-in features that can be combined to
produce arbitrary build configurations. The following command
builds the project's release variant with inlining
disabled and debug symbols enabled:
有多個內建的特性可以組合起來,生成不同的構建配置。以下命令行以禁止內聯和打開調試符號的配置來構建工程的 release 版本:
bjam release inlining=off debug-symbols=on
Properties on the command-line are specified with the syntax:
命令行上的屬性以這樣的語法指定:
feature-name=feature-value
The release and debug that we have seen
in bjam invocations are just a shorthand way to specify
values of the variant feature. For example, the
command above could also have been written this way:
我們在 bjam 調用中曾經見過的 release 和 debug 不過是一個指定 variant 特性的縮寫。例如,上述命令也可以寫成:
bjam variant=release inlining=off debug-symbols=on
variant is so commonly-used that it has been given
special status as an implicit feature—
Boost.Build will deduce the its identity just from the name of one of its
values.variant 是如此常用,所以它被給予了作為隱式特性的特殊狀態 —
Boost.Build 僅從它的值名就可以推斷出它的身份。
A complete description of features can be found in the section called 「Features and properties」.
有關"特性"的完整說明,請見 「特性與屬性」一節。
The set of properties specified on the command line constitute
a build request—a description of
the desired properties for building the requested targets (or,
if no targets were explicitly requested, the project in the
current directory). The actual
properties used for building targets are typically a
combination of the build request and properties derived from
the project's Jamroot (and its other
Jamfiles, as described in the section called 「Project Hierarchies」). For example, the
locations of #included header files are normally
not specified on the command-line, but described in
Jamfiles as target
requirements and automatically combined with the
build request for those targets. Multithread-enabled
compilation is another example of a typical target
requirement. The Jamfile fragment below
illustrates how these requirements might be specified.
在命令行中指定的一組屬性組成了 構建請求 — 用於構建目標(或者,如果沒有顯式給定目標,即當前目錄中的工程)的應有屬性的說明。用於構建目標的實際
屬性是構建請求和從工程的 Jamroot (及其它
Jamfiles,如 「工程層次」一節 中所說的)繼承得到的屬性的合併。例如,#included 頭文件的位置一般不在命令行中指定,卻會在
Jamfiles 中以 目標要求 描述,它會被自動與目標的構建請求合併。多線程編譯是目標要求的另一個典型例子。以下 Jamfile 片段示範了這些要求可以如何指定:
exe hello
: hello.cpp
: <include>boost <threading>multi
;
When hello is built, the two requirements specified
above will always be present. If the build request given on the
bjam command-line explictly contradicts a target's
requirements, the target requirements usually override (or, in the case
of 「free」」 features like
<include>,
[6]
augments) the build request.
當 hello 被構建,以上指定的兩個要求總是會被應用。如果在
bjam 命令行上給出的構建請求與某個目標要求相矛盾,則目標要求通常會覆蓋(或者加入到,像
<include> 這樣的"自由"特性[6] )構建請求。
![]() |
Tip 提示 |
|---|---|
The value of the
|
If we want the same requirements for our other target, hello2
, we could simply duplicate them. However, as projects grow,
that approach leads to a great deal of repeated boilerplate in Jamfiles.
Fortunately, there's a better way. Each project can specify a set of
attributes, including requirements:
如果我們對於另一個目標 hello2 也有相同的要求,我們只需要複製它們就可以了。不過,隨著工程的發展,這一方法會導致在 Jamfiles 中出現大量重複。幸好,我們有更好的方法。每個工程都可以指定一組 屬性attributes,包括目標要求:
project
: requirements <include>/home/ghost/Work/boost <threading>multi
;
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
The effect would be as if we specified the same requirement for both
hello and hello2.
其作用就像我們為
hello 和 hello2 指定相同的目標要求一樣。
So far we have only considered examples with one project —a. with
one user-written Boost.Jam file, Jamroot). A typical
large codebase would be composed of many projects organized into a tree.
The top of the tree is called the project root.
Every subproject is defined by a file called Jamfile
in a descendant directory of the project root. The parent project of a
subproject is defined by the nearest Jamfile or
Jamroot file in an ancestor directory. For example,
in the following directory layout:
迄今為止,我們只討論了帶有一個工程的例子 — 只帶有一個用戶編寫的 Boost.Jam 文件 Jamroot。一個典型的大型代碼基將包含許多工程,它們組成一棵樹。樹的頂端被稱為 工程的根。每一個子工程由工程根的一個子目錄中的名為 Jamfile
的文件來定義。一個子工程的父工程由其父目錄中最接近的 Jamfile 或
Jamroot 文件來定義。例如,在以下目錄佈局中:
top/
|
+-- Jamroot
|
+-- app/
| |
| +-- Jamfile
| `-- app.cpp
|
`-- util/
|
+-- foo/
. |
. +-- Jamfile
. `-- bar.cpp
the project root is top/. The projects in
top/app/ and top/util/foo/ are
immediate children of the root project.
工程的根是 top/. 在
top/app/ 和 top/util/foo/ 中的工程根工程的直接子工程。
![]() |
Note 說明 |
|---|---|
When we refer to a 「Jamfile,」 set in normal
type, we mean a file called either
當我們以普通方式使用 「Jamfile」 時,是指某個名為
|
Projects inherit all attributes (such as requirements)
from their parents. Inherited requirements are combined with
any requirements specified by the subproject.
For example, if top/Jamroot has
工程從它們的父工程繼承所有屬性(如目標要求)。繼承的要求與子工程指定的要求相合併。例如,如果 top/Jamroot 在目標要求中有:
<include>/home/ghost/local
in its requirements, then all of its subprojects will have it
in their requirements, too. Of course, any project can add
include paths to those specified by its parents. [7]
More details can be found in
the section called 「Projects」.
則它的所有子工程也都具有這一要求。當然,任一工程都可以在父工程的基礎上增加頭文件包含路徑[7]。更多的細節請見
「工程」一節。
Invoking bjam without explicitly specifying
any targets on the command line builds the project rooted in the
current directory. Building a project does not automatically
cause its subprojects to be built unless the parent project's
Jamfile explicitly requests it. In our example,
top/Jamroot might contain:
在命令行不指定任何目標地調用 bjam,將構建當前目錄中的工程。構建一個工程不會自動引起其子工程的構建,除非父工程的
Jamfile 中明確要求。在我們的例子中,top/Jamroot 可能包含有:
build-project app ;
which would cause the project in top/app/
to be built whenever the project in top/ is
built. However, targets in top/util/foo/
will be built only if they are needed by targets in
top/ or top/app/.
這將導致在構建 top/ 中的工程時,構建 top/app/
中的工程。但是,在 top/util/foo/
中的目標則僅當被
top/ 或 top/app/ 中的目標所需要時才構建。
When a building a target X depends on first
building another target Y (such as a
library that must be linked with X),
Y is called a
dependency of X and
X is termed a
dependent of Y.
如果目標 X 的構建依賴於另一個目標 Y 的首先構建(如一個必須與 X 鏈接的庫),則
Y 被稱為 X 的
依賴物,X 則被稱為 依賴於 Y。
To get a feeling of target dependencies, let's continue the
above example and see how top/app/Jamfile can
use libraries from top/util/foo. If
top/util/foo/Jamfile contains
為了獲得對目標依賴關係的感覺,我們來繼續前面的例子,看看 top/app/Jamfile 可以如何使用來自於 top/util/foo 的庫。如果
top/util/foo/Jamfile 包含有:
lib bar : bar.cpp ;
then to use this library in top/app/Jamfile, we can
write:
我們要使用這個庫,我們可以在 top/app/Jamfile 中寫:
exe app : app.cpp ../util/foo//bar ;
While app.cpp refers to a regular source file,
../util/foo//bar is a reference to another target:
a library bar declared in the Jamfile at
../util/foo.
其中 app.cpp 表示一個普通的源文件,../util/foo//bar 則表示另一個目標:在
../util/foo 的 Jamfile 中聲明的 bar 庫。
![]() |
Tip 提示 |
|---|---|
Some other build system have special syntax for listing dependent
libraries, for example |
Suppose we build app with:
假設我們要構建 app:
bjam app optimization=full define=USE_ASM
Which properties will be used to build foo? The answer is
that some features are
propagated—Boost.Build attempts to use
dependencies with the same value of propagated features. The
<optimization> feature is propagated, so both
app and foo will be compiled
with full optimization. But <define> is not
propagated: its value will be added as-is to the compiler flags for
a.cpp, but won't affect foo.
哪些屬性會用來構建 foo 呢? 答案是,有些特性是
被傳播的 — Boost.Build 嘗試以被傳播特性的相同值使用依賴物。特性
<optimization> 就是被傳播的,所以
app 和 foo 都會以全優化方式編譯。而 <define> 不是被傳播的:它的值會被作為
a.cpp 的一個編譯選項加入,但不會影響 foo.
Let's improve this project further. The library probably has some headers
that must be used when compiling app.cpp. We could
manually add the necessary #include paths to app
's requirements as values of the <include>
feature, but then this work will be repeated for all programs
that use foo. A better solution is to modify
util/foo/Jamfile in this way:
讓我們來改進一下這個工程。這個庫可能有一些頭文件在編譯 app.cpp 時要使用。我們可以手工將所需的 #include 路徑作為 <include>
特性的值加到 app 的要求中,但是這個工作稍後可能要對所有用到 foo 的程序都重複一次。一個更好的解決方法是,按以下方法修改 util/foo/Jamfile:
project
: usage-requirements <include>.
;
lib foo : foo.cpp ;
Usage requirements are applied not to the target being declared but to its
dependants. In this case, <include>. will be
applied to all targets that directly depend on foo.
"用法要求"不是應用到被聲明的目標上,而是應用到依賴於它的目標上。在這個例子中,<include> 將被應用到所有直接依賴於 foo 的目標上。
Another improvement is using symbolic identifiers to refer to the library,
as opposed to Jamfile location. In a large project, a
library can be used by many targets, and if they all use Jamfile
location, a change in directory organization entails much
work. The solution is to use project ids—symbolic names not tied to
directory layout. First, we need to assign a project id by adding this
code to Jamroot:
另一個改進是,使用符號標識符來表示一個庫,而不是用 Jamfile 的位置。在一個大型工程中,一個庫可以被多個目標使用,如果它們總是使用 Jamfile 位置來表示,那麼對目錄組織的修改將會帶來大量工作。解決方法是,使用工程 ids—符號名而不是依靠目錄佈局。首先,我們需要往 Jamroot 增加以下代碼來賦值一個工程 id:
use-project /library-example/foo : util/foo ;
Second, we modify app/Jamfile to use the project id:
然後,我們修改 app/Jamfile 以使用這個工程 id:
exe app : app.cpp /library-example/foo//bar ;
The /library-example/foo//bar syntax is used to refer
to the target bar in the project with id
/library-example/foo. We've achieved our goal—if the
library is moved to a different directory, only Jamroot
must be modified. Note that project ids are global—two
Jamfiles are not allowed to assign the same project id to different
directories.
語法 /library-example/foo//bar 是用於在工程中以 id
/library-example/foo 表示目標 bar。我們已經實現了我們的目標—如果庫被移動到其它目錄,只需要修改 Jamroot。注意,工程 ids 是全局的—不允許兩個
Jamfiles 將相同的工程 id 賦給不同的目錄。
![]() |
Tip 提示 |
|---|---|
|
If you want all applications in some project to link to a certain
library, you can avoid having to specify it directly the sources of
every target by using the 如果你想讓一些工程中的所有應用程序都鏈接到某個特定的庫,你可以無須直接在每個目標中使用 project |
Libraries can be either static, which means they are
included in executable files that use them, or shared
(a.k.a. dynamic), which are only referred to from
executables, and must be available at run time. Boost.Build can create and
use both kinds.
庫可以是 靜態的,即被包含在使用它們的可執行文件中,或者是 共享的
(又稱為 動態的),即在可執行文件中只有引用,在運行期才必須可用。對於這兩種類型,Boost.Build 可以創建和使用。
The kind of library produced from a lib target is determined
by the value of the link feature. Default value is
shared, and to build a static library, the value should
be static. You can request a static build either on the
command line:
一個 lib 目標所產生的庫的類型由特性 link 的值決定。缺省值是
shared, 要構建一個靜態庫,則其值應為 static。你可以有兩種方法請求構建一個靜態庫,在命令行上:
bjam link=static
或者在庫的要求中:
lib l : l.cpp : <link>static ;
We can also use the <link> property to express
linking requirements on a per-target basis. For example, if a particular
executable can be correctly built only with the static version of a
library, we can qualify the executable's target reference to the
library as follows:
我們也可以使用特性 <link> 來表示每個目標的鏈接要求。例如,如果某個可執行文件只能用某個庫的靜態版本來構建,我們可以限定可執行文件的 目標引用 到這個庫,如下:
exe important : main.cpp helpers/<link>static ;
No matter what arguments are specified on the bjam
command line, important will only be linked with the
static version of helpers.
無論在 bjam
命令行中指定什麼參數,important 都只會與 helpers 的靜態版本鏈接。
Specifying properties in target references is especially useful if you use
a library defined in some other project (one you can't change) but you
still want static (or dynamic) linking to that library in all cases. If
that library is used by many targets, you could use
target references everywhere:
如果你使用了一個在其它工程(一個你不能修改的工程)中定義的庫,而你還是想在任何情況下都進行靜態(或動態)鏈接,那麼在目標要求中指定特性就非常有用了。如果那個庫被多個目標使用,你可以在任意地方使用目標引用:
exe e1 : e1.cpp /other_project//bar/<link>static ;
exe e10 : e10.cpp /other_project//bar/<link>static ;
but that's far from being convenient. A better approach is to introduce a
level of indirection. Create a local alias target that refers
to the static (or dynamic) version of foo:
但這樣很不方便。更好的方法是引用一個間接層。創建一個局部別名目標來表示 foo 的靜態(或動態)版本:
alias foo : /other_project//bar/<link>static ;
exe e1 : e1.cpp foo ;
exe e10 : e10.cpp foo ;
The alias
rule is specifically used to rename a reference to a target and
possibly change the properties.
規則 alias 特別適用於修改一個目標引用的名字,也可以修改特性。
![]() |
Tip 提示 |
|---|---|
|
When one library uses another, you put the second library in the source
list of the first. For example: lib utils : utils.cpp /boost/filesystem//fs ;
This works no matter what kind of linking is used. When |
![]() |
Note 說明 |
|---|---|
(Note for non-UNIX system). Typically, shared libraries must be
installed to a directory in the dynamic linker's search path. Otherwise,
applications that use shared libraries can't be started. On Windows, the
dynamic linker's search path is given by the |
Sometimes, particular relationships need to be maintained among a target's
build properties. For example, you might want to set specific
#define when a library is built as shared, or when a target's
release variant is built. This can be achieved using
conditional requirements.
有時候,某些特殊關係需要根據目標的構建屬性來維護。例如,你可能想在某個庫被構建為共享庫時,或者在構建一個目標的 release 版本時,設置特定的
#define。這可以通過
條件要求 來實現。
lib network : network.cpp
: <link>shared:<define>NEWORK_LIB_SHARED <variant>release:<define>EXTRA_FAST ;
In the example above, whenever network is built with
<link>shared, <define>NEWORK_LIB_SHARED
will be in its properties, too. Also, whenever its release variant
is built, <define>EXTRA_FAST will appear in its
properties.
在上例中,當 network 以
<link>shared 構建時,<define>NEWORK_LIB_SHARED
將會在它的屬性中。同樣,當構建它的發佈版本時,<define>EXTRA_FAST 將會出現在它的屬性中。
Sometimes the ways a target is built are so different that describing them
using conditional requirements would be hard. For example, imagine that a
library actually uses different source files depending on the toolset used
to build it. We can express this situation using target
alternatives:
有時候一個目標的構建方法差異很大,很難用條件要求來描述。例如,想像一個根據構建工具集不同而使用不同源文件的庫。我們可以用 目標選擇 來表示這種情形:
lib demangler : dummy_demangler.cpp ; # alternative 1
lib demangler : demangler_gcc.cpp : <toolset>gcc ; # alternative 2
lib demangler : demangler_msvc.cpp : <toolset>msvc ; # alternative 3
When building demangler, Boost.Build will compare
requirements for each alternative with build properties to find the best
match. For example, when building with <toolset>gcc
alternative 2, will be selected, and when building with
<toolset>msvc alternative 3 will be selected. In all
other cases, the most generic alternative 1 will be built.
在構建 demangler 時,Boost.Build 將每種選擇與構建屬性進行比較,找出最佳匹配。例如,在使用 <toolset>gcc
進行構建時,選擇 alternative 2,而在使用
<toolset>msvc 進行構建時,則選擇 alternative 3。在其它情況下,最通用的 alternative 1 將被構建。
To link to libraries whose build instructions aren't given in a Jamfile,
you need to create lib targets with an appropriate
file property. Target alternatives can be used to
associate multiple library files with a single conceptual target. For
example:
要鏈接到那些構建指令沒有在 Jamfile 中給定的庫時,你需要以一個適當的 file 屬性創建 lib 目標。可以使用目標選擇來將多個庫文件關聯至一個概念上的目標。例如:
# util/lib2/Jamfile
lib lib2
:
: <file>lib2_release.a <variant>release
;
lib lib2
:
: <file>lib2_debug.a <variant>debug
;
This example defines two alternatives for lib2, and
for each one names a prebuilt file. Naturally, there are no sources.
Instead, the <file> feature is used to specify
the file name.
這個例子為 lib2 定義了兩個選擇,為每個分別指定預構建文件。這裡自然是沒有源文件的。而是使用 <file> 特性來指定文件名。
Once a prebuilt target has been declared, it can be used just like any
other target:
一旦聲明了一個預構建目標,它就可以像其它任意目標一樣使用:
exe app : app.cpp ../util/lib2//lib2 ;
As with any target, the alternative selected depends on the properties
propagated from lib2's dependants. If we build the
release and debug versions of app will be linked
with lib2_release.a and lib2_debug.a
, respectively.
和其它目標一樣,目標的選擇是根據從依賴於 lib2 的目標所傳播來的屬性進行的。如果我們構建 app 的發佈版本和調試版本,則它們分別與 lib2_release.a 和 lib2_debug.a
鏈接。
System libraries—those that are automatically found by the toolset
by searching through some set of predetermined paths—should be
declared almost like regular ones:
系統庫—它們由工具集在一組預定義的路徑中自動查找—應該像普通的庫一樣聲明:
lib pythonlib : : <name>python22 ;
We again don't specify any sources, but give a name
that should be passed to the compiler. If the gcc toolset were used to
link an executable target to pythonlib,
-lpython22 would appear in the command line (other
compilers may use different options).
我們不用指定任何源文件,但是要給定一個傳遞給編譯器的 name。如果使用 gcc 工具集來鏈接一個可執行目標到 pythonlib,則 -lpython22 將出現在命令行中(其它編譯器可能使用不同的選項)。
We can also specify where the toolset should look for the library:
我們也可以指定工具在哪裡查找該庫:
lib pythonlib : : <name>python22 <search>/opt/lib ;
And, of course, target alternatives can be used in the usual way:
當然還有,目標選擇可以按普通方法使用:
lib pythonlib : : <name>python22 <variant>release ;
lib pythonlib : : <name>python22_d <variant>debug ;
A more advanced use of prebuilt targets is described in the section called 「Targets in site-config.jam」.
有關預構建目標的更進一步使用,在 「site-config.jam 中的目標」一節 中說明。
[7] Many
features will be overridden,
rather than added-to, in subprojects. See the section called 「Feature Attributes」 for more
information
在子工程中的許多特性都是覆寫的,而不是增加的。更多信息請見 「特性屬性」一節。