Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Extender Manual 擴展手冊

Introduction 簡介
Target types 目標類型
Tools and generators 工具和生成器
Features 特性
Main target rules 主目標規則
Toolset modules 工具集模塊

Introduction 簡介

This document explains how to extend Boost.Build to accomodate your local requirements. Let's start with a simple but realistic example.
本文檔介紹如何擴展 Boost.Build 以適應你的本地化需求。讓我們從一個簡單而現實的例子開始。

Say you're writing an application that generates C++ code. If you ever did this, you know that it's not nice. Embedding large portions of C++ code in string literals is very awkward. A much better solution is:
假設你正在編寫一個生成 C++ 代碼的應用。如果你曾經試過,你應該知道那並不好辦。將大部分 C++ 代碼嵌入到字符串中是非常笨拙的。一個更好的解決方法是:

  1. Write the template of the code to be generated, leaving placeholders at the points that will change
    編寫被生成代碼的模板,在將會變化的地方放置佔位符。
  2. Access the template in your application and replace placeholders with appropriate text.
    在你的應用中訪問這些模板,並以適當的文本替換佔位符。
  3. Write the result.
    輸出結果。

It's quite easy to achieve. You write special verbatim files that are just C++, except that the very first line of the file contains the name of a variable that should be generated. A simple tool is created that takes a verbatim file and creates a cpp file with a single char* variable whose name is taken from the first line of the verbatim file and whose value is the file's properly quoted content.
這很容易辦到。你編寫一些特定的 C++ 逐字文件,只不過這些文件的第一行含有要被生成的變量的名稱。你可以創建一個簡單的工具,它接受一個逐字文件,生成一個 cpp 文件和一個 char* 變量,變量名取自於逐字文件的第一行,而變量值則是文件的正確內容。

Let's see what Boost.Build can do.
我們來看看 Boost.Build 可以做什麼。

First off, Boost.Build has no idea about "verbatim files". So, you must register a new target type. The following code does it:
首先,Boost.Build 不知道"逐字文件"。所以,你必須註冊一個新的目標類型。以下代碼可以做到:

import type ;
type.register VERBATIM : verbatim ;

The first parameter to type.register gives the name of the declared type. By convention, it's uppercase. The second parameter is the suffix for files of this type. So, if Boost.Build sees code.verbatim in a list of sources, it knows that it's of type VERBATIM.
傳給 type.register 的第一個參數給出被聲明類型的名稱。作為慣例,它應該是大寫的。第二個參數為此類文件的後綴。所以,如果 Boost.Build 在源列表中看到 code.verbatim,它知道其類型為 VERBATIM

Next, you tell Boost.Build that the verbatim files can be transformed into C++ files in one build step. A generator is a template for a build step that transforms targets of one type (or set of types) into another. Our generator will be called verbatim.inline-file; it transforms VERBATIM files into CPP files:
接著,你要告訴 Boost.Build 逐字文件可以在一個構建步驟中轉化為 C++ 文件。生成器 是一個用於在一個構建步驟中將一種類型(或一組類型)的目標轉化為另一種類型的目標的模板。我們的生成器將被稱為 verbatim.inline-file;它將 VERBATIM 文件轉化為 CPP 文件:

import generators ;
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;

Lastly, you have to inform Boost.Build about the shell commands used to make that transformation. That's done with an actions declaration.
最後,你必須告訴 Boost.Build 用於執行這一轉化的 shell 命令。這是用一個 actions 聲明來完成的。

actions inline-file
{
"./inline-file.py" $(<) $(>)
}

Now, we're ready to tie it all together. Put all the code above in file verbatim.jam, add import verbatim ; to Jamroot.jam, and it's possible to write the following in your Jamfile:
現在,我們可以將所有東西合起來。將以上所有代碼放入文件 verbatim.jam,再將 import verbatim ; 加到 Jamroot.jam 中,就可以在你的 Jamfile 中編寫以下代碼:

exe codegen : codegen.cpp class_template.verbatim usage.verbatim ;

The listed verbatim files will be automatically converted into C++ source files, compiled and then linked to the codegen executable.
以上列出的逐字文件將被自動轉換為 C++ 源文件,被編譯然後鏈接成 codegen 可執行文件。

In subsequent sections, we will extend this example, and review all the mechanisms in detail. The complete code is available in the example/customization directory.
在以下章節中,我們將擴展這一例子,並回顧這一機制的所有細節。完整的代碼位於 example/customization 目錄中。

Target types 目標類型

The first thing we did in the intruduction was declaring a new target type:
簡介 中我們所做的第一件事是,聲明一個新的目標類型:

import type ;
type.register VERBATIM : verbatim ;

The type is the most important property of a target. Boost.Build can automatically generate necessary build actions only because you specify the desired type (using the different main target rules), and because Boost.Build can guess the type of sources from their extensions.
類型是一個目標最重要的屬性。Boost.Build 可以自動生成所需的構建動作,就是因為你給出了想要的類型(使用不同的主目標規則),或是因為 Boost.Build 可以從源文件的擴展名猜測到源文件的類型。

The first two parameters for the type.register rule are the name of new type and the list of extensions associated with it. A file with an extension from the list will have the given target type. In the case where a target of the declared type is generated from other sources, the first specified extension will be used.
type.register 規則的頭兩個參數是新類型的名字以及與之關聯的擴展名列表。一個帶有列表中某個擴展名的文件具有給定的目標類型。當被聲明類型的目標是生成自其它源目標時,將使用第一個指定的擴展名。

Sometimes you want to change the suffix used for generated targets depending on build properties, such as toolset. For example, some compiler uses extension elf for executable files. You can use the type.set-generated-target-suffix rule:
有時,你可能想根據構建屬性,如工具集等,來改變被生成目標的後綴。例如,有些編譯器對可執行文件使用 elf 擴展名。這時,你可以使用 type.set-generated-target-suffix 規則:

type.set-generated-target-suffix EXE : <toolset>elf : elf ;

A new target type can be inherited from an existing one.
新的目標類型也可以從已有的類型繼承:

type.register PLUGIN : : SHARED_LIB ;

The above code defines a new type derived from SHARED_LIB. Initially, the new type inherits all the properties of the base type - in particular generators and suffix. Typically, you'll change the new type in some way. For example, using type.set-generated-target-suffix you can set the suffix for the new type. Or you can write special a generator for the new type. For example, it can generate additional metainformation for the plugin. In either way, the PLUGIN type can be used whenever SHARED_LIB can. For example, you can directly link plugins to an application.
上述代碼定義了一個繼承自 SHARED_LIB 的新類型。起初,新類型繼承了基類型的所有屬性 - 尤其是生成器和後綴。通常情況下,你會在某種程度上修改這個新類型。例如,通過使用 type.set-generated-target-suffix,你可以設置新類型的後綴。或者可以專門為新類型編寫一個生成器。例如,可以為插件生成額外的元信息。無論是哪種方式,類型 PLUGIN 可以在任何使用 SHARED_LIB 的地方使用。例如,你可以直接將插件鏈接到一個應用程序中。

A type can be defined as "main", in which case Boost.Build will automatically declare a main target rule for building targets of that type. More details can be found later.
類型可以被定義為"main",這時 Boost.Build 將為該類型的目標構建自動聲明一個主目標規則。更多細節請見 後文

Scanners 掃瞄器

Sometimes, a file can refer to other files via some include system. To make Boost.Build track dependencies between included files, you need to provide a scanner. The primary limitation is that only one scanner can be assigned to a target type.
有時候,一個文件可以通過某些包含系統引向其它文件。要讓 Boost.Build 跟蹤包含文件之間的依賴關係,你需要提供一個掃瞄器。主要的限制是,對於一種目標類型,只能賦給一個掃瞄器。

First, we need to declare a new class for the scanner:
首先,我們需要為掃瞄器聲明一個新的類:

class verbatim-scanner : common-scanner
{
rule pattern ( )
{
return "//###include[ ]*\"([^\"]*)\"" ;
}
}

All the complex logic is in the common-scanner class, and you only need to override the method that returns the regular expression to be used for scanning. The parentheses in the regular expression indicate which part of the string is the name of the included file. Only the first parenthesized group in the regular expression will be recognized; if you can't express everything you want that way, you can return multiple regular expressions, each of which contains a parenthesized group to be matched.
所有的複雜邏輯都在 common-scanner 類中,你只需要覆寫一個返回用於掃瞄的正則表達式的方法。在正則表達式中的圓括號表示其中的字符串是被包含文件的名字。只有在正則表達式中的第一個括號組 被識別;如果你不能以此方法表達你想要的東西,你可以返回多個正則表達式,其中每個正則表達式都包含一個用於匹配的括號組。

After that, we need to register our scanner class:
然後,我們需要註冊我們的掃瞄器類:

scanner.register verbatim-scanner : include ;

The value of the second parameter, in this case include, specifies the properties that contain the list of paths that should be searched for the included files.
第二個參數的值,即本例中的 include,給出了一些屬性,其中包含有查找被包含文件的各個路徑的列表。

Finally, we assign the new scanner to the VERBATIM target type:
最後,我們將這個新的掃瞄器賦給 VERBATIM 目標類型:

type.set-scanner VERBATIM : verbatim-scanner ;

That's enough for scanning include dependencies.
這就足以掃瞄包含的依賴關係了。

Tools and generators 工具和生成器

This section will describe how Boost.Build can be extended to support new tools.
本節將講述 Boost.Build 可以如何擴展以支持新的工具。

For each additional tool, a Boost.Build object called generator must be created. That object has specific types of targets that it accepts and produces. Using that information, Boost.Build is able to automatically invoke the generator. For example, if you declare a generator that takes a target of the type D and produces a target of the type OBJ, when placing a file with extention .d in a list of sources will cause Boost.Build to invoke your generator, and then to link the resulting object file into an application. (Of course, this requires that you specify that the .d extension corresponds to the D type.)
對於每一種新的工具,必須創建一個名為生成器的 Boost.Build 對象。該對像接受和產生特定的目標類型。通過使用這些信息,Boost.Build 可以自動調用生成器。例如,如果你聲明了一個生成器,它接受類型為 D 的目標並產生類型為 OBJ 的目標,那麼在處理一個在源列表中的帶有擴展名 .d 的文件時,將引發 Boost.Build 調用你的生成器,然後將所得到的目標文件鏈接到應用程序中。(當然,這需要你指定擴展名 .d 對應於類型 D)。

Each generator should be an instance of a class derived from the generator class. In the simplest case, you don't need to create a derived class, but simply create an instance of the generator class. Let's review the example we've seen in the introduction.
每一個生成器都應該是一個派生自 generator 類的類的一個實例。在最簡單的情況下,你不需要創建一個派生類,只要創建一個 generator 類的實例就可以了。我們來重溫一下在 簡介 中看到的例子。

import generators ;
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
actions inline-file
{
"./inline-file.py" $(<) $(>)
}

We declare a standard generator, specifying its id, the source type and the target type. When invoked, the generator will create a target of type CPP with a source target of type VERBATIM as the only source. But what command will be used to actually generate the file? In bjam, actions are specified using named "actions" blocks and the name of the action block should be specified when creating targets. By convention, generators use the same name of the action block as their own id. So, in above example, the "inline-file" actions block will be used to convert the source into the target.
我們聲明了一個標準的生成器,指定它的 id、源類型和目標類型。在被調用時,生成器會以類型為 VERBATIM 的源目標為唯一源創建一個類型為 CPP 的目標。不 過,用什麼命令來實際生成這個文件呢?在 bjam 中,動作是用命名的 "actions" 塊來指定的,在創建目標時應給出動作塊的名稱。為方便起見,生成器使用了和它們本身的 id 同名的動作塊。所以,在上例中,"inline-file" 動作塊將被用於將源轉換為目標。

There are two primary kinds of generators: standard and composing, which are registered with the generators.register-standard and the generators.register-composing rules, respectively. For example:
有兩類主要的生成器:標準的和組合的,它們分別使用 generators.register-standard 規則和 generators.register-composing 規則來註冊。例如:

generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
generators.register-composing mex.mex : CPP LIB : MEX ;

The first (standard) generator takes a single source of type VERBATIM and produces a result. The second (composing) generator takes any number of sources, which can have either the CPP or the LIB type. Composing generators are typically used for generating top-level target type. For example, the first generator invoked when building an exe target is a composing generator corresponding to the proper linker.
第一個(標準的)生成器接受單一個 VERBATIM 類型的源並產生一個結果。第二個(組合的)生成器則接受任意數量的源,可以是 CPP 類型或 LIB 類型。組合生成器通常用於生成頂層的目標類型。例如,在構建一個 exe 目標時第一個被調用的生成器就是與正確的鏈接器相對應的生成器。

You should also know about two specific functions for registering generators: generators.register-c-compiler and generators.register-linker. The first sets up header dependecy scanning for C files, and the seconds handles various complexities like searched libraries. For that reason, you should always use those functions when adding support for compilers and linkers.
你還應知道有兩個用於註冊生成器的特殊函數:generators.register-c-compilergenerators.register-linker。第一個掃瞄 C 文件以設置頭文件依賴關係,第二個則處理多種的複雜性,如查找庫。為此,你在增加對編譯器和鏈接器的支持時應該總是使用這些函數。

(Need a note about UNIX)
(需要一個有關 UNIX 的說明)

Custom generator classes 定制生成器類

The standard generators allows you to specify source and target types, an action, and a set of flags. If you need anything more complex, you need to create a new generator class with your own logic. Then, you have to create an instance of that class and register it. Here's an example how you can create your own generator class:
標準的生成器允許你指定類類型和目標類型、一個動作和一組標誌。如果你需要更複雜的東西,你就需要創建一個新的、具有你自己的邏輯的生成器類。然後,你必須創建這個類的一個實例並註冊它。以下是一個如何創建自己的生成器類的例子:

class custom-generator : generator
{
rule __init__ ( * : * )
{
generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
}

}

generators.register
[ new custom-generator verbatim.inline-file : VERBATIM : CPP ] ;

This generator will work exactly like the verbatim.inline-file generator we've defined above, but it's possible to customize the behaviour by overriding methods of the generator class.
這個生成器的作用與我們在前面定義的 verbatim.inline-file 生成器完全一樣,不過它通過覆寫 generator 類中的方法,可以定制相應的行為。

There are two methods of interest. The run method is responsible for the overall process - it takes a number of source targets, converts them to the right types, and creates the result. The generated-targets method is called when all sources are converted to the right types to actually create the result.
有兩個要關注的方法。方法 run 負責整個過程 - 它接受多個源目標,將它們轉換為正確的類型,並創建結果。方法 generated-targets 則在所有源都被轉換為正確類型時被調用,以正確創建結果。

The generated-targets method can be overridden when you want to add additional properties to the generated targets or use additional sources. For a real-life example, suppose you have a program analysis tool that should be given a name of executable and the list of all sources. Naturally, you don't want to list all source files manually. Here's how the generated-targets method can find the list of sources automatically:
在你想增加其它屬性到生成的目標中,或者使用其它源時,可以覆寫 generated-targets 方法。以下是一個現實的例子,假設你有一個程序分析工具,要給定它一個可執行文件的名字和所有源的列表。自然,你不會想手工列出所有源文件。以下就是 generated-targets 方法如何可以自動找到源列表的方法:

class itrace-generator : generator {
....
rule generated-targets ( sources + : property-set : project name ? )
{
local leaves ;
local temp = [ virtual-target.traverse $(sources[1]) : : include-sources ] ;
for local t in $(temp)
{
if ! [ $(t).action ]
{
leaves += $(t) ;
}
}
return [ generator.generated-targets $(sources) $(leafs)
: $(property-set) : $(project) $(name) ] ;
}
}
generators.register [ new itrace-generator nm.itrace : EXE : ITRACE ] ;

The generated-targets method will be called with a single source target of type EXE. The call to virtual-target.traverse will return all targets the executable depends on, and we further find files that are not produced from anything. The found targets are added to the sources.
方法 generated-targets 被調用時會傳入一個 EXE 類型的源目標。對 virtual-target.traverse 的調用將返回該可執行文件所依賴的所有目標,我們再進一步找出那些不是從其它目標生成的文件。找到的目標將被增加到源中。

The run method can be overriden to completely customize the way the generator works. In particular, the conversion of sources to the desired types can be completely customized. Here's another real example. Tests for the Boost Python library usually consist of two parts: a Python program and a C++ file. The C++ file is compiled to Python extension that is loaded by the Python program. But in the likely case that both files have the same name, the created Python extension must be renamed. Otherwise, the Python program will import itself, not the extension. Here's how it can be done:
方法 run 可以被覆寫以完全定制生成器工作的方式。具體地說,源目標到所要類型的轉換可以被完全定制。以下是另一個真實的例子。Boost Python 庫的測試通常包含兩個部分:一個 Python 程序和一個 C++ 文件。C++ 文件被編譯為 Python 擴展庫,並由 Python 程序裝入。但是在多數情況下,這兩個文件會具有一樣的名字,所以所創建的 Python 擴展庫必須更名。否則,Python 程序將裝入自己本身,而不是擴展庫。以下是如何做的方法:

rule run ( project name ? : property-set : sources * )
{
local python ;
for local s in $(sources)
{
if [ $(s).type ] = PY
{
python = $(s) ;
}
}

local libs ;
for local s in $(sources)
{
if [ type.is-derived [ $(s).type ] LIB ]
{
libs += $(s) ;
}
}

local new-sources ;
for local s in $(sources)
{
if [ type.is-derived [ $(s).type ] CPP ]
{
local name = [ $(s).name ] ; # get the target's basename 取得目標的基本名
if $(name) = [ $(python).name ]
{
name = $(name)_ext ; # rename the target 對目標改名
}
new-sources += [ generators.construct $(project) $(name) :
PYTHON_EXTENSION : $(property-set) : $(s) $(libs) ] ;
}
}

result = [ construct-result $(python) $(new-sources) : $(project) $(name)
: $(property-set) ] ;
}

First, we separate all source into python files, libraries and C++ sources. For each C++ source we create a separate Python extension by calling generators.construct and passing the C++ source and the libraries. At this point, we also change the extension's name, if necessary.
首先,我們將所有源文件分為 python 文件、庫文件和 C++ 源文件。對於每個 C++ 源文件,我們通過調用 generators.construct 並傳入 C++ 源文件和庫文件,創建一個獨立的 Python 擴展庫。同時,我們還要在需要時修改該擴展庫的名字。

Features 特性

Often, we need to control the options passed the invoked tools. This is done with features. Consider an example:
通常,我們需要控制傳遞給被調用工具的選項。這是通過特性來完成的。考慮以下例子:

# Declare a new free feature 聲明一個新的自由特性
import feature : feature ;
feature verbatim-options : : free ;

# Cause the value of the 'verbatim-options' feature to be
# available as 'OPTIONS' variable inside verbatim.inline-file
# 使得 'verbatim-options' 特性的值成為 verbatim.inline-file 內的 'OPTIONS' 變量
import toolset : flags ;
flags verbatim.inline-file OPTIONS <verbatim-options> ;

# Use the "OPTIONS" variable 使用 'OPTIONS' 變量
actions inline-file
{
"./inline-file.py" $(OPTIONS) $(<) $(>)
}

We first define a new feature. Then, the flags invocation says that whenever verbatin.inline-file action is run, the value of the verbatim-options feature will be added to the OPTIONS variable, and can be used inside the action body. You'd need to consult online help (--help) to find all the features of the toolset.flags rule.
我們首先定義一個新的特性。然後,對 flags 的調用表示無論何時運行 verbatin.inline-file 動作,verbatim-options 特性的值都將被加入到 OPTIONS 變量中,並可以在動作體內部使用。你需要參考在線幫助 (--help) 以查找 toolset.flags 規則的所有特性。

Although you can define any set of features and interpret their values in any way, Boost.Build suggests the following coding standard for designing features.
雖然你可以定義任意的特性集,並以任何方式解釋它們的值,但是 Boost.Build 為特性的設計建議了以下代碼標準。

Most features should have a fixed set of values that is portable (tool neutral) across the class of tools they are designed to work with. The user does not have to adjust the values for a exact tool. For example, <optimization>speed has the same meaning for all C++ compilers and the user does not have to worry about the exact options passed to the compiler's command line.
多數特性都應該具有一個固定的值集,這些值應該可以在想與該特性一起使用的工具類之間移植(即與工具無關)。用戶不需要為特定的工具調整這些值。例如,對於所有的 C++ 編譯器而言,<optimization>speed 具有相同的意義,用戶無須關心傳遞給編譯器的命令行的精確選項。

Besides such portable features there are special 'raw' features that allow the user to pass any value to the command line parameters for a particular tool, if so desired. For example, the <cxxflags> feature allows you to pass any command line options to a C++ compiler. The <include> feature allows you to pass any string preceded by -I and the interpretation is tool-specific. (See the section called 「Can I get output of external program as a variable in a Jamfile? 」 for an example of very smart usage of that feature). Of course one should always strive to use portable features, but these are still be provided as a backdoor just to make sure Boost.Build does not take away any control from the user.
除了這些可移植的特性以外,還有一些特殊的'原始'特性,它允許用戶在需要時傳遞任意值給特定工具的命令行參數。例如,特性 <cxxflags> 允許你傳遞任意命令行選項給 C++ 編譯器。特性 <include> 則允許你傳遞任意字符串並前加 -I,對它的解釋是工具指定的。(有關這一特性的一個非常漂亮的用例,參見 「我可以獲取外部程序的輸出並用作 Jamfile 的變量嗎?」一節)。當然,你應該盡量使用可移植特性,但這些特性還是作為一個後門被提供,以確保 Boost.Build 不會使用戶失去控制力。

Using portable features is a good idea because:
使用可移植特性是好的方式,因為:

  • When a portable feature is given a fixed set of values, you can build your project with two different settings of the feature and Boost.Build will automatically use two different directories for generated files. Boost.Build does not try to separate targets built with different raw options.
    因為可移植特性給定了固定的值集,你可以使用不同的特性設置來構建你的工程,而 Boost.Build 會自動為生成的文件使用不同的目錄。Boost.Build 不會將以不同的原始選項進行構建的目標分開。

  • Unlike with 「raw」 features, you don't need to use specific command-line flags in your Jamfile, and it will be more likely to work with other tools.
    與"原始"特性不同,你不需要在你的 Jamfile 中使用特定的命令行選項,這樣它更可能和其它工具一起使用。

Steps for adding a feauture 增加一個特性的步驟

Adding a feature requires three steps:
增加一個特性需要三個步驟:

  1. Declaring a feature. For that, the "feature.feature" rule is used. You have to decide on the set of feature attributes:
    聲明一個特性。為此,要使用 "feature.feature" 規則。你必須決定 特性屬性的集合

    • if you want a feature value set for one target to automaticaly propagate to its dependant targets then make it 「propagated」.
      如果你希望某個目標的一個特性值集自動傳播給依賴於它的目標,則將它標記為 "propagated".

    • if a feature does not have a fixed list of values, it must be 「free.」 For example, the include feature is a free feature.
      如果一個特性沒有固定的值列表,它就必須是"自由"的。例如,include 特性就是一個自由特性。

    • if a feature is used to refer to a path relative to the Jamfile, it must be a 「path」 feature. Such features will also get their values automatically converted to Boost Build's internal path representation. For example, include is a path feature.
      如果一個特性用於表示相對於 Jamfile 的路徑,它就必須是一個"路徑"特性。此類特性將它們的值自動轉換為 Boost Build 的內部路徑表示法。例如,include 就是一個路徑特性。

    • if feature is used to refer to some target, it must be a 「dependency」 feature.
      如果一個特性用於表示某個目標,它必須是一個"依賴"特性。

  2. Representing the feature value in a target-specific variable. Build actions are command templates modified by Boost.Jam variable expansions. The toolset.flags rule sets a target-specific variable to the value of a feature.
    在一個目標特定的變量中表示這個特性值。構建動作是一些通過 Boost.Jam 變量展開修改的命令模板。toolset.flags 規則將一個目標特定變量設置為特性的值。

  3. Using the variable. The variable set in step 2 can be used in a build action to form command parameters or files.
    使用該變量。在第2步中設置的變量可以在構建動作中使用,形成命令參數或文件。

Another example 另一個例子

Here's another example. Let's see how we can make a feature that refers to a target. For example, when linking dynamic libraries on Windows, one sometimes needs to specify a "DEF file", telling what functions should be exported. It would be nice to use this file like this:
以下是另一個例子。我們來看看如何可以讓一個特性引用一個目標。例如,在 Windows 上鏈接動態庫時,有時候需要指定一個 "DEF 文件",說明哪些函數要被導出。像下面這樣使用這個文件是好的方式:

        lib a : a.cpp : <def-file>a.def ;

Actually, this feature is already supported, but anyway...
實際上,這個特性已經被支持,不過...

  1. Since the feature refers to a target, it must be "dependency".
    因為這個特性引向一個目標,它必須是"依賴"的。

    feature def-file : : free dependency ;

  2. One of the toolsets that cares about DEF files is msvc. The following line should be added to it.
    一個關心 DEF 文件的工具集是 msvc。應加入以下這行代碼。

    flags msvc.link DEF_FILE <def-file> ;

  3. Since the DEF_FILE variable is not used by the msvc.link action, we need to modify it to be:
    由於 msvc.link 動作沒有使用 DEF_FILE 變量,我們需要將它修改為:

    actions link bind DEF_FILE
    {
    $(.LD) .... /DEF:$(DEF_FILE) ....
    }

    Note the bind DEF_FILE part. It tells bjam to translate the internal target name in DEF_FILE to a corresponding filename in the link action. Without it the expansion of $(DEF_FILE) would be a strange symbol that is not likely to make sense for the linker.
    留意 bind DEF_FILE 部分。它告訴 bjam 將 DEF_FILE 中的內部目標名翻譯為在 link 動作中的對應文件名。沒有它,$(DEF_FILE) 的展開將是一個一點都不像鏈接器的奇怪符號。

    We are almost done, but we should stop for a small workaround. Add the following code to msvc.jam
    我們快完成了,不過還要為一個小的變通方法停一會。把以下代碼增加到 msvc.jam 中

    rule link
    {
    DEPENDS $(<) : [ on $(<) return $(DEF_FILE) ] ;
    }

    This is needed to accomodate some bug in bjam, which hopefully will be fixed one day.
    這是為了適應 bjam 中的一些缺陷而需要的,有希望在以後修正。

Variants and composite features. 變體與合成特性

Sometimes you want to create a shortcut for some set of features. For example, release is a value of <variant> and is a shortcut for a set of features.
有時你想要為一些特性集創建一個縮寫。例如,release<variant> 的一個值,也是一個特性集的縮寫。

It is possible to define your own build variants. For example:
你可以定義自己的構建變體。例如:

variant crazy : <optimization>speed <inlining>off
<debug-symbols>on <profiling>on ;

will define a new variant with the specified set of properties. You can also extend an existing variant:
就定義了一個帶有指定屬性集的新變體。你也可以擴展已有的變體:

variant super_release : release : <define>USE_ASM ;

In this case, super_release will expand to all properties specified by release, and the additional one you've specified.
在這個例子中,super_release 將展開為由 release 指定的所有屬性,再加上你指定的那個屬性。

You are not restricted to using the variant feature only. Here's example that defines a brand new feature:
你並沒有被限制只能使用 variant 特性。以下是一個定義全新特性的例子:

feature parallelism : mpi fake none : composite link-incompatible ;
feature.compose <parallelism>mpi : <library>/mpi//mpi/<parallelism>none ;
feature.compose <parallelism>fake : <library>/mpi//fake/<parallelism>none ;

This will allow you to specify the value of feature parallelism, which will expand to link to the necessary library.
它允許你指定特性 parallelism 的值,該值將展開為鏈接所需的庫。

Main target rules 主目標規則

A main target rule (e.g 「exe」 Or 「lib」) creates a top-level target. It's quite likely that you'll want to declare your own and there are two ways to do that.
主目標規則(如 「exe」 或 「lib」)創建一個頂層目標。很有可能你想聲明自己的主目標,有兩種方法可以做到。

The first way applies when your target rule should just produce a target of specific type. In that case, a rule is already defined for you! When you define a new type, Boost.Build automatically defines a corresponding rule. The name of the rule is obtained from the name of the type, by downcasing all letters and replacing underscores with dashes. For example, if you create a module obfuscate.jam containing:
第一種方法適用於你的目標規則只是產生指定類型的一個目標的情況。在這種情況下,已經為你定義了一個規則!當你定義一個新的類型時,Boost.Build 自動地定義了一個相應的規則。該規則的名字來自於類型的名字,將所有字母變為小寫並將下劃線替換為連字符而得到。例如,如果你創建了一個模塊 obfuscate.jam,其中包含:

import type ;
type.register OBFUSCATED_CPP : ocpp ;

import generators ;
generators.register-standard obfuscate.file : CPP : OBFUSCATED_CPP ;

and import that module, you'll be able to use the rule "obfuscated-cpp" in Jamfiles, which will convert source to the OBFUSCATED_CPP type.
導入該模塊,你將可以在 Jamfiles 中使用規則 "obfuscated-cpp",它會將源目標轉換為 OBFUSCATED_CPP 類型。

The second way is to write a wrapper rule that calls any of the existing rules. For example, suppose you have only one library per directory and want all cpp files in the directory to be compiled into that library. You can achieve this effect using:
第二種方法是,編寫一個調用已有規則的外裹規則。例如,假定你在每個目錄中只有一個庫,而你想讓目錄中的所有 cpp 文件都編譯到這個庫中。你可以用以下方法實現這一目的:

lib codegen : [ glob *.cpp ] ;

If you want to make it even simpler, you could add the following definition to the Jamroot.jam file:
如果你想更簡單些,你可以將以下定義加到 Jamroot.jam 文件中:

rule glib ( name : extra-sources * : requirements * )
{
lib $(name) : [ glob *.cpp ] $(extra-sources) : $(requirements) ;
}

allowing you to reduce the Jamfile to just
它允許你將 Jamfile 簡化為:

glib codegen ;

Note that because you can associate a custom generator with a target type, the logic of building can be rather complicated. For example, the boostbook module declares a target type BOOSTBOOK_MAIN and a custom generator for that type. You can use that as example if your main target rule is non-trivial.
注意,因為你可以將一個定制的生成器關聯到一個目標類型,所以構建的邏輯可能會很複雜。例如,模塊 boostbook 聲明了一個目標類型 BOOSTBOOK_MAIN,以及一個用於該類型的定制生成器。如果你的主目標規則是非平凡的,你可以像上例那樣使用。

Toolset modules 工具集模塊

If your extensions will be used only on one project, they can be placed in a separate .jam file and imported by your Jamroot.jam. If the extensions will be used on many projects, users will thank you for a finishing touch.
如果你的擴展只在一個工程中使用,那麼它們可以被放置在一個獨立的 .jam 文件中,並由你的 Jamroot.jam 導入。如果擴展會在多個工程中使用,那麼用戶會非常感激你的最後這一步工作。

The using rule provides a standard mechanism for loading and configuring extensions. To make it work, your module should provide an init rule. The rule will be called with the same parameters that were passed to the using rule. The set of allowed parameters is determined by you. For example, you can allow the user to specify paths, tool versions, and other options.
using 規則為擴展的裝入和配置提供了標準的機制。要使用它,你的模塊應提供一個 init 規則。該規則將被以傳給 using 規則的相同參數來調用。允許使用的參數集是由你決定的。例如,你可以允許用戶指定路徑、工具版本和其它選項。

Here are some guidelines that help to make Boost.Build more consistent:
以下是有助於讓 Boost.Build 更加一致的一些指引:

  • The init rule should never fail. Even if the user provided an incorrect path, you should emit a warning and go on. Configuration may be shared between different machines, and wrong values on one machine can be OK on another.
    init 規則永遠不應失敗。即使用戶指定了一個錯誤的路徑,你應該給出一個警告並繼續。配置可以在不同機器間共享,在一台機器上錯誤的值可能在另一台上是OK的。

  • Prefer specifying the command to be executed to specifying the tool's installation path. First of all, this gives more control: it's possible to specify
    最好給出可以指明工具的安裝路徑的執行命令。首先,這可以有更多的控制:你可以指定

    /usr/bin/g++-snapshot
    time g++

    as the command. Second, while some tools have a logical "installation root", it's better if the user doesn't have to remember whether a specific tool requires a full command or a path.
    為該命令。其次,雖然一些工具有一個合乎邏輯的"安裝根",但如果用戶不記得是否某個特定工具是否需要完整的命令或路徑,這樣做更好一些。

  • Check for multiple initialization. A user can try to initialize the module several times. You need to check for this and decide what to do. Typically, unless you support several versions of a tool, duplicate initialization is a user error. If the tool's version can be specified during initialization, make sure the version is either always specified, or never specified (in which case the tool is initialied only once). For example, if you allow:
    檢 查多次初始化。用戶可以多次初始化一個模塊。你需要檢查這一點,並決定如何做。通常,除非你支持一個工具的多個版本,否則重複的初始化就是一個用戶錯誤。 如果工具的版本可以在初始化時指定,請確保版本總是被指定,或從不指定(這種情況下,工具只能初始化一次)。例如,如果你允許:

    using yfc ;
    using yfc : 3.3 ;
    using yfc : 3.4 ;

    Then it's not clear if the first initialization corresponds to version 3.3 of the tool, version 3.4 of the tool, or some other version. This can lead to building twice with the same version.
    那麼,這弄不清楚第一個初始化對應的是版本3.3的工具,還是版本3.4的工具,或是其它的版本。這會導致同一版本的兩次構建。

  • If possible, init must be callable with no parameters. In which case, it should try to autodetect all the necessary information, for example, by looking for a tool in PATH or in common installation locations. Often this is possible and allows the user to simply write:
    如果可以,init 必須可以以不帶參數的方式調用。這時,它應嘗試自動檢測所有必需的信息,例如,在 PATH 或通常的安裝位置中查找工具。通常這是可能的,並允許用戶只要寫:

    using yfc ;

  • Consider using facilities in the tools/common module. You can take a look at how tools/gcc.jam uses that module in the init rule.
    考慮使用在 tools/common 模塊中的工具。你可以看一下 tools/gcc.jaminit 規則中是如何使用這個模塊的。


PrevUpHomeNext