由 Boris Schäling 編寫。
Boost.Build 是一個高階編譯系統,讓管理 C++ 專案變得盡可能簡單。其概念是僅在組態檔中指定用於編譯程式所需的內容即可。例如,您不需要告訴 Boost.Build 如何使用某個特定編譯器。Boost.Build 內建支援多種編譯器,並知道如何使用它們。如果您建立一個組態檔,您只需要告訴 Boost.Build 程式碼檔案位在哪裡,可執行檔應該稱為什麼,以及 Boost.Build 應該使用哪個編譯器。Boost.Build 隨後會嘗試尋找編譯器並自動編譯程式。
由於 Boost.Build 支援多個編譯器,因此組態檔永遠不包含任何特定於編譯器的選項。組態檔完全與編譯器無關。當然可以設定選項,例如是否要最佳化程式碼。但是,這些選項是用 Boost.Build 才能理解的語言編寫的。在選取編譯器用於編譯程式後,Boost.Build 會將組態檔中的選項轉譯為選取編譯器預期的命令列選項。這讓您可以編寫一次組態檔,並使用不同的編譯器在不同的平台上編譯程式。
Boost.Build 雖然聽起來很棒,但只能用於 C++ 和 C 專案。Boost.Build 不知道如何使用其他編譯器,例如 Java 編譯器。儘管 Boost.Build 是可擴充的,但對使用其他程式語言實作的程式來說,使用不同的編譯系統會是更有意義的選擇。
建立 Boost.Build 的目的是使用不同的編譯器輕鬆地在不同的平台上編譯和安裝 Boost C++ 函式庫。雖然 Boost.Build 是 Boost C++ 函式庫的一部分,並隨附在其中,但它可以獨立用於任何 C++ 或 C 專案。您甚至可以 單獨下載 Boost.Build,如果您不想使用 Boost C++ 函式庫的話。
本文是使用 Boost.Build 製作 C++ 或 C 專案的入門介紹。文章將基本說明 Boost.Build 的運作方式,以及如何開始使用。閱讀完本文後,您不只能為自己的專案使用 Boost.Build,您還能更容易瞭解Boost.Build 文件,因為您對整體架構已略有所知。
用於建置 Boost.Build 管理專案的程式稱為 b2。如果您已下載並建置 Boost C++ 函式庫,代表您已經使用過 b2。 b2 會尋找組態檔、讀取組態檔並依其建置專案。它也會接受各種命令列選項,這些選項可能有些用途,例如顯示 b2 執行的全部指令,以建置專案。
專案可能龐大,且可能包含許多元件,這些元件的原始碼分散在多個目錄中。相較於為整個專案建立一個龐大的組態檔,專案元件通常會取得自己的組態檔。Boost.Build 也一樣:在一個大型專案中,將會有許多組態檔,而 b2 必須找出這些組態檔並加以處理。
對於 Boost.Build 而言,每個具有組態檔的目錄都是一個專案:如果一個目錄中有組態檔,代表可以建置某些項目。在 Boost.Build 中,不管該項目是子目錄中的某個元件、或是由許多元件組成的軟體,其實沒有差別。
當 b2 開始執行時,它並不會在整個檔案系統中搜尋組態檔。它只會在目前的作業目錄中搜尋組態檔。如果找不到組態檔,程式便不會執行任何動作。 b2 絕不會在其他目錄中搜尋組態檔,前提是目前的作業目錄中並不存在組態檔。
b2 正在尋找的組態檔案稱為 Jamfile.jam
。副檔名為 jam
的檔案稱為「Jamfile」。若 b2 在目前的作業目錄中找到 Jamfile,它會在父目錄中搜尋更多 Jamfile。b2 會持續往上搜尋父目錄,直到找到稱為 Jamroot.jam
的組態檔案為止。Jamroot.jam
與 Jamfile.jam
並沒有不同,它只表示 b2 不需要再繼續搜尋。
b2 在父目錄中尋找 Jamfile 的原因,在於這樣可以將設定進行分組。如果有某些元件需要使用類似的設定來建置,這些元件就可以儲存在父目錄中的 Jamfile 中,如果在子目錄中建置某個元件,將自動使用該檔案。
請注意,b2 一定會找到稱為 Jamroot.jam
的檔案,否則會發生錯誤。如果 Jamroot.jam
位於目前的作業目錄中,則不需要其他 Jamfile.jam
檔案。如果 Jamroot.jam
位於父目錄中,則 Jamfile.jam
檔案必須存在於目前的作業目錄中,否則 b2 不會執行任何動作。
如果您將 b2 複製到不包含任何 Jamfile 的目錄中並啟動程式,您會收到一條錯誤訊息。不過,b2 並不會抱怨它找不到 Jamfile,它會抱怨找不到建置系統。
Unable to load Boost.Build: could not find "boost-build.jam" --------------------------------------------------------------- Attempted search from C:\Users\Boris\Desktop up to the root Please consult the documentation at 'https://boost.dev.org.tw'.
b2 要執行的第一件事不是尋找 Jamfile,而是載入建置系統。但是,建置系統究竟是什麼呢?
b2 是一個詮譯器。它本身並不清楚如何建置任何東西。b2 所做的事情是詮釋 Jamfile。Boost.Build 實際上是以 Jamfile 實作的,而且它們包含促使 Boost.Build 成為強大工具的各種邏輯。由於 b2 只會執行它在 Jamfile 中讀取的內容,因此它需要知道 Boost.Build 組成要在哪裡找到 Jamfile。
當 b2 啟動時,它會在目前的作業目錄中尋找 boost-build.jam
檔案。如果它找不到這個檔案,它會搜尋所有父目錄。這個檔案只需要包含一行,告訴 b2 在哪裡可以找到建置系統。
boost-build C:/boost_1_57_0/tools/build/src ;
在 boost-build
之後的路徑必需要參考包含有 bootstrap.jam
檔案的目錄。這是檔案 b2 需要載入建置系統的。由於 Boost C++ 函式庫已裝運 Boost.Build,因此你可以參考 Boost C++ 函式庫根目錄 tools/build
的子目錄。你總可以使用斜線作為路徑分隔符號 - 即使你在 Windows 上也是如此。
請注意在路徑與行末的分號之間必須要有空白。如果沒有這個空白會是一個錯誤。你將在這篇文章後面學到更多關於 Jamfile 中使用的語法。
如果 b2 找到了 boost-build.jam
檔案,它就會使用檔案中的路徑來載入建置系統。當載入建置系統時,它也會準備好使用特定編譯器、連結器及其他可能用於建置專案的工具。Boost.Build 將這些程式稱為工具組。如果在啟動 b2 時沒有使用任何命令列選項,建置系統就會嘗試找出它可以自動使用的工具組。例如在 Windows 上,它會搜尋 Visual C++。如果偵測到已安裝 Visual C++,它就會使用 msvc 工具組。
warning: No toolsets are configured. warning: Configuring default toolset "msvc". warning: If the default is wrong, your build may not work correctly. warning: Use the "toolset=xxxxx" option to override our guess. warning: For more configuration options, please consult warning: https://boost.dev.org.tw/boost-build2/doc/html/bbv2/advanced/configuration.html
如果你在沒有指定應該使用哪個工具組的情況下啟動 b2,你會看到一個警告。 b2 會告訴你它偵測到了哪個工具組,並決定要使用哪個工具組。如果你想略過此警告,就必須自己指定工具組。例如,你告訴建置系統使用 Visual C++ 的方式是 b2 toolset=msvc。如果你想使用 GCC,就要輸入 b2 toolset=gcc。
到目前為止,支援超過 10 個工具組。很可能 Boost.Build 可以立刻與你使用的編譯器搭配使用。
一旦建置系統被找到、載入,而且知道要使用哪個工具組 - 無論是你指定一個或是建置系統自動偵測到一個 - b2 會在目前的作業目錄中尋找 Jamfile.jam
檔案。如果它找不到 Jamfile,就會印出一個錯誤訊息。
error: error: no Jamfile in current directory found, and no target references specified.
如果你建立一個空的 Jamfile.jam
檔案,然後重新啟動 b2,就會印出另一個錯誤訊息。
error: Could not find parent for project at '.' error: Did not find Jamfile.jam or Jamroot.jam in any parent directory.
b2 最終要尋找稱為 Jamroot.jam
的 Jamfile。如果在目前的作業目錄中不存在這個檔案,b2 預期會在父目錄中找到它。
如果你建立一個空的 Jamroot.jam
檔案,然後啟動 b2,錯誤訊息就會消失了。顯然 Boost.Build 沒有執行任何動作。但現在你知道 b2 如何繼續建置程式,以及 Boost.Build 基本設定會是什麼樣子。
請注意,如果你只有一個中小型專案且只有一個組態檔時,你可以叫它為 Jamroot.jam
。你不需要另一個叫 Jamfile.jam
的檔案。
如果你研究過 Jamfiles,它的語法或許會讓你聯想到其他編譯系統使用的組態檔。簡短的 Jamfiles 看起來就像一般的組態檔,其中的數值似乎會指派給金鑰。但重要的是,Jamfiles 事實上是指令碼檔案。它有一門程式語言可以用於撰寫 Jamfiles。 b2 並不是 Boost.Build 的核心元件,它並不知道如何編譯程式。Boost.Build 的邏輯是在 Jamfiles 中,讓 b2 知道如何編譯程式。
即使 Boost.Build 是基於一門程式語言,你在建立 Jamfiles 的時候不需要有程式方面的概念。Boost.Build 使用的程式語言語法試圖讓你更像是在建立一般的組態檔。我們的想法是同時享受兩個好處:一種強大且彈性的程式語言,以及你可能從其他編譯系統認識的簡單語法。
這篇文章不會介紹 Boost.Build 所依賴的程式語言。這門程式語言是自有的,而且用起來並不令人滿意。它比不上像 Javascript 或 Python 等受歡迎的指示碼語言。Boost.Build 的開發人員也意識到這點,並且正在製作另一種基於 Python 的 Boost.Build。但這一切對於規劃使用 Boost.Build 管理專案的開發人員來說並不重要。了解 Jamfiles 的語法有所幫助,特別是在你意識到 Boost.Build 內部有一門程式語言之後。但你不需要了解這門程式語言的細節。
讓我們看一個可以從來源檔案 hello.cpp
編譯可執行檔 hello 的簡單 Jamfile。
exe hello : hello.cpp ;
Boost.Build 提供許多內建規則,而 exe
就是其中一個。Boost.Build 的文件將 exe
稱為規則,但你已經知道上面的 Jamfile 其實是使用一門程式語言寫成的。正如你所見,規則其實就是函式。而上面的 Jamfile 就包含了一個函式呼叫。
在典型的建置程式中,Boost.Build 提供預定義規則 - 或者如果你願意,也可以稱為函式。就如同其他程式語言中的函式,也可以傳遞引數。在上面的 Jamfile 中,函式 exe
帶兩個引數 hello 和 hello.cpp 被呼叫。
Boost.Build 所基於的程式語言僅認識一種資料型態:所有東西都是字串清單。清單可以是空的,或是包含一個或多個字串。在上面的 Jamfile 中,函式 exe
帶兩個引數被呼叫,每個引數都是一個包含一個字串的清單。
exe "hello" : "hello.cpp" ;
可以使用引號。然而這是沒有必要的,畢竟清單中的每個項目都是字串資料型態。引號只用於引數包含空白時。
雖然規則和第一個引數之間沒有特殊分隔字,但是必須使用冒號來分隔其他引數。還必須使用分號來結束行,這就像你使用 C++ 時一樣。
請注意 Boost.Build 的程式語言要求在所有標記間必須有空白。例如,在冒號左右必須有空白,且在分號之前必須有空白。如果標記間沒有空白,b2 將無法正確解析 Jamfile。
如果在包含上面 Jamfile 和原始檔 hello.cpp
的目錄中執行 b2,且在 Windows 上使用 msvc 工具組,就會建立一個子目錄 bin\msvc-9.0\debug
來建置可執行檔 hello.exe
。
...found 9 targets... ...updating 5 targets... common.mkdir bin common.mkdir bin\msvc-9.0 common.mkdir bin\msvc-9.0\debug compile-c-c++ bin\msvc-9.0\debug\hello.obj hello.cpp msvc.link bin\msvc-9.0\debug\hello.exe msvc.manifest bin\msvc-9.0\debug\hello.exe ...updated 5 targets...
如你所見,在 Jamfile 中只需要一行就可以從原始檔建置一個可執行檔。如果程式是在 Windows 上建置,甚至會附加正確的副檔名 exe
。
Boost.Build 的主要優點是你只須提供建置系統必要的資訊以建置程式。Boost.Build 可以自動完成的任何事情,都會自動完成。你不必偵測程式所建置的平台,來決定是否要附加副檔名 exe
。而且你不必指定像 Visual C++ 這類編譯器是如何被呼叫來編譯原始碼。
Boost.Build 出廠預設就支援許多工具組。由於一個程式可以使用不同的工具組來建置,因此 Boost.Build 使用特定於工具組的目錄。透過這個方式,可以使用不同的工具組來建置程式,而不會發生工具組不斷覆寫其他工具組所產生的檔案的狀況。
不只有特定於工具組的目錄,還有特定於變樣的目錄。變樣就是程式的除錯或釋出版本。會為每個變樣使用另一個目錄來建置程式,同樣的用意是避免覆寫其他變樣產生的檔案。預設使用除錯變樣。那就是為什麼會建立 bin\msvc-9.0\debug
子目錄的原因。如果您想要建置釋出版本,可以在指令列中使用 b2 variant=release 或更簡單地使用 b2 release 來指定變樣。
...found 9 targets... ...updating 5 targets... common.mkdir bin common.mkdir bin\msvc-9.0 common.mkdir bin\msvc-9.0\release compile-c-c++ bin\msvc-9.0\release\hello.obj hello.cpp msvc.link bin\msvc-9.0\release\hello.exe msvc.manifest bin\msvc-9.0\release\hello.exe ...updated 5 targets...
將變樣設定為釋出後,會使用 bin\msvc-9.0\release
子目錄來建置可執行檔 hello.exe
。
選擇變樣十分常見,因此輸入 b2 release 就夠了。Boost.Build 會明白釋出是用來選擇變樣的。
如果您不想在指令列中指定變樣,但想要預設建置 hello.exe
的釋出版本,就必須變更 Jamfile。
exe hello : hello.cpp : <variant>release ;
exe
規則(或者如果您喜歡的話,就是函式)接受一些額外的參數,它們是可選的。第三個參數是一個需求清單。您可以把它想成總是設定的指令列選項,而且傳遞給建置可執行檔所執行的命令。
為了強制建置釋出版本,必須將變樣設定為釋出,就像先前在指令列中設定的一樣。儘管如此,在 Jamfile 中設定變樣的語法不同。
Boost.Build 定義類似 XML 標記的功能。Boost.Build 支援的功能之一是 <variant>
。如果應該將功能設定為值,則必須將值放在功能旁邊,中間不要有空格。有些功能是自由的,表示它們可以設定為您想要的任何值。 <variant>
不是自由功能,因為它只能設定為 debug 或 release。不允許使用其他值。如果設定其他值,b2
會回報錯誤。
如果您執行 b2 variant=debug
並嘗試建置 hello.exe
的除錯版本,因為 Jamfile 包含 hello.exe
應該建置為釋出版本的條件,所以它不會運作。如果您想要覆寫指令列上的功能,必須將功能傳遞為第四個參數,而不是第三個。
exe hello : hello.cpp : : <variant>release ;
第四個參數包含預設使用的功能,但可以使用覆寫。
若要同時預設建置 hello.exe
的 debug 與 release 版本,需要將 <variant>
功能設定為 debug 及 release 兩次。
exe hello : hello.cpp : : <variant>debug <variant>release ;
請務必在第四個參數(指定預設值的位置)中設定 <variant>
兩次。如果設定在第三個參數(指定需求的位置),b2 會回報錯誤。雖然可以在需求中多次設定功能,但前提是不會彼此互斥。由於程式無法同時為 debug 及 release 版本,<variant>
必需設定在預設值中。只有這樣,Boost.Build 才會清楚了解需要建置 hello.exe
的兩個版本。
exe hello : hello.cpp : <define>WIN32 <define>_WIN32 : <variant>debug <variant>release ;
上述 Jamfile 是在需求設定中多次設定功能範例。功能 <define>
用於定義預處理程式指令。定義多個預處理程式指令不成問題。因此會建置兩個 hello.exe
版本,並在兩個版本中定義指令 WIN32
及 _WIN32
。
exe hello : hello.cpp : : <variant>debug <variant>release <define>WIN32 <define>_WIN32 ;
如果將定義移至第四個參數,並執行 b2,系統會建置兩個具有指令 WIN32
及 _WIN32
的 hello.exe
版本。由於 <define>
並未預設互斥值,因此不會產生其他可執行檔案。此 Jamfile 與前一個 Jamfile 的唯一不同之處,在於第四個參數中的指令傳遞為可刪除的預設值;而第三個參數中傳遞的任何內容均為不可變需求。
以下列出另一個值彼此互斥的功能範例。
exe hello : hello.cpp : : <variant>debug <variant>release <optimization>speed <optimization>off ;
b2 建立 hello.exe
的四個版本:針對速度最佳化的 debug 版本、未最佳化的 debug 版本、針對速度最佳化的 release 版本及未最佳化的 release 版本。所有這些版本均建置於自動建立的獨立目錄中。
到目前為止,僅使用 exe
規則。不過,Boost.Build 當然提供了多項內建規則。另一個重要的規則為 lib
。此規則用於建置函式庫。
lib world : world.cpp ;
上述 Jamfile 從來源檔案 world.cpp
建置共用函式庫。系統會在 Windows 作業系統中建立 world.dll
檔案。Boost.Build 會再次自動附加一般的檔案副檔名。
預設會建置共用函式庫。如果你想要產生靜態函式庫,請將 <link>
功能設為靜態。
lib world : world.cpp : <link>static ;
另一個有用的規則是 install
。在可執行檔和函式庫建置完成後,可以使用這個規則來安裝它們。
exe hello : hello.cpp ; install "C:/Program Files/hello" : hello ;
以上 Jamfile 會將可執行檔 hello.exe
安裝到目錄 C:\Program Files\hello
。第二個參數 hello 是對第一行定義的 hello 目標的參考。請注意路徑必須加上引號,因為路徑中含有空白。
這裡閃耀著其他建置系統已知概念:不需要考慮函式呼叫,每行都定義一個目標。相依性建立於參照其他目標時。這是 Boost.Build 知道它應該依什麼順序進行建置目標的方式。
不過,規則 install
通常寫法不同。它不會將安裝目錄傳遞為第一個參數,而是使用 <location>
功能在第三個參數設定安裝目錄。
exe hello : hello.cpp ; install install-bin : hello : <location>"C:/Program Files/hello" ;
使用 <location>
較佳的主要原因是,第一個參數總是定義目標。其他規則可能會參照目標。這是使用之後不必變更目標名稱的好方法。想像一個程式應該安裝到不同的目錄。假如已使用 <location>
功能,變更安裝目錄會比較容易,因為不必更新其他可能參照 install-bin 的規則。
還有使用功能的另一個原因。Boost.Build 支援條件屬性,可讓我們根據建置程式的平台使用不同的安裝目錄。
exe hello : hello.cpp ; install install-bin : hello : <target-os>windows:<location>"C:/Program Files/hello" <target-os>linux:<location>/usr/local/bin ;
<target-os>
功能是另一個互斥值的屬性。它可以設定為例如 windows 或 linux,但不能同時設定為 windows 與 linux。
<location>
功能遵循 <target-os>
,中間僅以冒號分隔。這樣的結構稱為條件屬性:Boost.Build 會根據作業系統來選擇安裝目錄。
當然,條件屬性也可以和其他規則一起使用。例如,在建置程式或函式庫時,可以根據變異定義不同的預處理器指示。
Boost.Build 提供更多其他內建規則。另一個有用的規則是 glob
,它允許使用萬用字元。在含有許多原始檔的大型專案中,就不需要將所有原始檔逐一列出來,而是可以使用 glob
參照所有原始檔。
exe hello : [ glob *.cpp ] ;
上述的 Jamfile 含有巢狀函數呼叫:規則 glob
的結果傳遞為 exe
的第二個參數。由於在 Boost.Build 所依賴的程式語言的需求下,巢狀函數呼叫必須使用括號。
對於許多含有大量 Jamfiles 的大型專案而言,以某種方式連接 Jamfiles 是必要的。專案的根目錄通常有一個 Jamroot.jam
檔案,而在子目錄下有許多 Jamfile.jam
檔案。如果 b2 是在根目錄執行的,開發人員可能會期望建置包含所有元件在子目錄內的整個專案。由於 b2 會在父目錄中尋找 Jamfiles,但不會在子目錄中,所以 Jamfiles 需要明確地參照子目錄中的 Jamfiles。
build-project hello ;
如果 Jamfile 看起來就像上面的範例,它會參照到子目錄 hello
中的 Jamfile。build-project
是一個規則,其唯一的參數預期是一個路徑。然後,路徑會用來尋找 Jamfile。
build-project hello ; build-project world ;
如果您想要建置多個專案,您必須多次使用 build-project
。
除了參照子目錄內的 Jamfiles 之外,將在建置專案元件時應該使用的選項分組起來也很有意義。
project : default-build release ; build-project hello ; build-project world ;
project
規則接受各種參數,以設定目前工作目錄和子目錄中 Jamfile 的選項。
雖然其他規則,例如 exe
和 lib
,預期參數會按著某個順序傳入,project
會使用具名稱的參數。在上面的範例中,參數的名稱是 default-build。這就是為什麼可以在非常不同的參數中傳遞 release 值的原因。
project : : : : : : : : : default-build release ; build-project hello ; build-project world ;
將 release 傳遞為第十個參數並沒有意義。但它可以使用,因為 project
不在乎順序。由於第十個參數稱為 default-build,所以它是可以被接受的。
project
僅支援少數幾個具名稱的參數。另一個參數是 requirements,它可用來設定無法覆寫的選項。
project : requirements <variant>release ; build-project hello ; build-project world ;
上面的 Jamfile 僅建置發行版本。不再能夠建置偵錯版本,因為 requirements 無法被覆寫。這與前面範例中使用的稱為 default-build 的具名稱參數不同:它可以被覆寫。
當使用 build-project
時,Boost.Build 會假設參數是對子目錄的參照。我們以前看過另一種類型的參照。
exe hello : hello.cpp ; install install-bin : hello : <location>"C:/Program Files/hello" ;
在上面的 Jamfile 中,install
規則參照第一行定義的目標 hello。
在大型專案中,可能需要參照在不同目錄中的 Jamfiles 中定義的目標。可以使用雙斜線連結路徑到 Jamfile 和目標。
install install-bin : subdir//hello : <location>"C:/Program Files/hello" ;
現在 install
規則參照 subdir
子目錄的 Jamfile 中的目標 hello。
假設可執行檔 hello 相依於另一個目錄 world
中的函式庫。而這個函式庫也使用 lib
規則,以 Boost.Build 進行建置。
lib world : world.cpp ;
在產生可執行檔的 Jamfile 中,需要參照函式庫的 Jamfile。無須直接參照目標 world,因為 Jamfile 中所有的目標預設都會建置。
exe hello : hello.cpp world : : <variant>debug <variant>release ;
上述的 Jamfile 假設函式庫及對應的 Jamfile 在 world
子目錄中。
在建置可執行檔時,會產生兩個版本 - 偵錯版本與發行版本。然而函式庫的 Jamfile 並未設定 <variant>
功能。但 Boost.Build 假設它也應該建置函式庫的兩個版本。<variant>
功能稱為宣傳。
功能宣傳簡化了專案管理,因為您無須在各種 Jamfile 中設定相同的功能。然而,由於所有情況都取決於哪些功能宣傳,這也讓了解元件如何建置變得更加複雜。您可以假設 Boost.Build 知道應執行哪些動作。但這當然不表示您可以輕易了解它執行了哪些動作。
讓我們來看另一個使用 <define>
功能的範例。
exe hello : hello.cpp world : <define>WIN32 : <variant>debug <variant>release ;
上述的 Jamfile 為程式 hello 定義了預處理器指令 WIN32
。但 WIN32
是否也會為這個函式庫定義呢?
不會,因為 <define>
不是宣傳功能。如果您想知道應如何得知這一點:找出哪些功能宣傳的唯一方法,就是查閱文件。
如果您安裝了 Boost C++ 函式庫,您或許會想要連結到其中一些函式庫。您必須以某種方式將對應的 Boost C++ 函式庫相依性新增到專案的 Jamfile。如果您沒有刪除已解壓縮的 Boost C++ 函式庫原始檔案的目錄,則可以參照根目錄的 Jamfile 中的目標。
exe hello : hello.cpp world C:/boost_1_39_0//filesystem/ ;
現在 hello 還依賴於 Boost.Filesystem 類庫。由於目標檔案系統已在 Boost C++ 類庫根目錄的 Jamfile 中定義,所以 exe
規則可參照它。系統不只會連結適當的 Boost C++ 類庫,還會將 include 目錄傳遞給編譯器尋找標頭檔案。如果 hello.cpp
包含 boost/filesystem.hpp
,系統就會找到標頭檔案。
在上述的 Jamfile 中,通往 Boost C++ 類庫根目錄的路徑是硬編碼的。不知何故,b2 需要知道 Boost C++ 類庫在哪裡。不過,如果專案中有多個組件需要連結至某些 Boost C++ 類庫,最好只將路徑硬編碼一次。
project : requirements <variant>release ; use-project /boost : C:/boost_1_39_0 ; build-project hello ; build-project world ;
use-project
規則用於定義另一個目錄中 Jamfile 的別名。然後,子目錄中的 Jamfile 便可使用該別名參照 Boost C++ 類庫。
exe hello : hello.cpp world /boost//filesystem ;
b2 發現 hello.cpp
是來源檔案,world
是子目錄,而 /boost//filesystem 是對 C:\boost_1_39_0
中的 Jamfile 中一個目標檔案系統的參考。
請注意,如果參考項是要指向專案,則必須以斜線開頭。
由於可以採用不同的方式連結類庫,因此可以設定與連結器相關的功能。
exe hello : hello.cpp world /boost//filesystem/<link>static ;
預設情況下,系統會動態連結類庫。如果要靜態連結類庫,則必須將 <link>
功能設為靜態。
功能可用斜線附加。如果要設定多項功能,請在先前的功能後面附加另一個斜線。
exe hello : hello.cpp world /boost//filesystem/<link>static/<threading>multi ;
<threading>
是一個可設為單一或多重的其他功能。如果 hello 要連結至 Boost.Filesystem 的執行緒安全版本,就可適當地設定該功能。
透過參照 Jamfile 來連結 Boost C++ 類庫並不總是都能順利進行。如果以不同的方式安裝了 Boost C++ 類庫,例如它們並未從來源建立,則不會有任何 Jamfile 可供參照。
lib filesystem : : <name>libboost_filesystem <search>C:/libs ; exe hello : hello.cpp world filesystem : <include>C:/include ;
lib
規則不僅可從來源建置類庫。它也可用於參照現有的已建置類庫。
如果 lib
不應該從來源建置類庫,第二個參數必須為空。相反地,第三個參數中,<name>
和 <search>
功能用於指定類庫的名稱和 Boost.Build 將會找到類庫的位置。
使用獨立於平台的方式指定類庫的名稱非常重要。例如,對於上述的 Jamfile,Boost.Build 將會嘗試在 Windows 上尋找 libboost_filesystem.lib
檔案。系統會自動附加一般的檔案副檔名。
如果您想要透過指定精確名稱參照檔案,可以使用 <file>
功能。
若系統函式庫已參考了,預期 Boost.Build 會知道在哪裡可以找到它,此功能 <search>
可以移除。
也可以使用 project
規則,以確保專案中的所有目標都會自動與函式庫建立連結。
lib filesystem : : <name>libboost_filesystem <search>C:/libs ; explicit filesystem ; project : requirements <include>C:/include <library>filesystem ; lib world : world.cpp ;
包含 <library>
功能是加入函式庫相依性的project
規則。<library>
必須參考 lib
規則,此規則使用已知的 <name>
及<search>
功能。
現在很重要的是讓 lib
規則明確。請使用explicit
規則來執行此動作。設定明確很重要,因為預設 Jamfile 中的所有目標都會建置出來。project
規則定義 Jamfile 中所有目標的需求,因此這些需求也是lib
規則的需求。因此 lib
規則會參考到它自己。但如果 lib
規則明確設定,就不會被建置出來,也不會發生遞迴參考。
請注意 Jamfile 中規則的順序只有在規則參考目標時才重要:目標在被參考之前都必須先定義出來。
Boost.Build 是高層級建置系統,如果您能讓 Jamfile 與平台和編譯器保持相依性,就可以獲得最大的好處。所有的點子最後都是要在不必修改或維護多個 Jamfile 的情況下,使用任何編譯器在任何平台上建置您的 C++ 或 C 專案。
您可能會遇到的典型問題是有意使用的第三方函式庫會安裝在不同的目錄中。如果您想在 Windows 和 Unix 平台上建置專案,路徑也會看起來非常不同。此外,您可能需要在一個平台上連結某些系統函式庫,卻不需要在另一個平台連結。
請不要嘗試在專案的 Jamfile 中加入不同平台的路徑。更好的作法是,在每個系統上都根據系統特定設定來依賴設定檔。事實證明,b2 在開始時確實會尋找兩個以上的設定檔。
檔案 site-config.jam
應設定為整個系統選項。由於 b2 會依據不同的機器而有所不同,因此預設 Windows 平台在 C:\Windows
找到它,Unix 系統則在 /etc
找到它。site-config.jam
會依據機器不同,因此本機函式庫的路徑不是問題。
使用者的確可能無法建立或編輯 site-config.jam
。他們可能會需要等到系統管理員更新檔案或被迫再次為自己的 Jamfiles 新增系統專屬路徑。由於這兩種解法都不是理想的解決方案,因此 b2 也會在使用者的家目錄中尋找 user-config.jam
檔案。在 Windows 上,它會是 C:\Users
的子目錄,而在 Unix 上,則會是 /home
的子目錄。由於 user-config.jam
檔案可以由使用者維護,因此可能會比 site-config.jam
檔案更常使用。
您可以像使用其他 Jamfiles 一樣使用 site-config.jam
和 user-config.jam
。由於這些設定檔不屬於專案,而是屬於系統或系統上的使用者,因此可以包含與機器相關的選項。舉例來說,它們可以包含 using
規則。
using msvc ;
上方 using
規則會告知 b2 要使用 msvc 工具集。如果您知道系統中只安裝了 Visual C++,將這行文字加入設定檔中是很合理的。這樣一來,b2 就無需再猜測要使用哪個工具集,也不會漏發警告訊息。
如果您在 site-config.jam
或 user-config.jam
中定義目標,並想要在 Jamfiles 中參照這些目標,就必須使用 project
規則設定名稱。
using msvc ; project user-config ; lib xml : : <name>libxml <search>C:/lib : : <include>C:/include ;
lib
規則用來參照預先建置的函式庫,該函式庫的基礎檔名為 libxml,且可以在 C:\lib
中找到。使用此 XML 函式庫的程式可能需要包含該函式庫中的標頭檔。這是為何在使用需求 (這是第五個參數) 中,<include>
功能設定為 C:\include
:使用這項規則的人員將會繼承 <include>
功能。
由於 project
規則已用來設定名稱 user-config,因此 Jamfile 可以透過 /user-config//xml 來參照 XML 函式庫。
exe xmlparser : xmlparser.cpp : <library>/user-config//xml ;
為了建置 xmlparser,系統必須連結 XML 函式庫。儘管函式庫及其標頭檔的位置可能會有所不同,但 Jamfile 中並沒有包含任何系統專屬的路徑。Jamfile 預期會在 user-config 專案中找到 xml 目標。如果這是設定檔,使用系統專屬路徑不成問題,因為所有的設定檔都繫結到系統或是系統上的使用者。
Boost.Build 建立於建立和安裝 Boost C++ 程式庫,故內建支援,讓您可以更容易使用預先建置的 Boost C++ 程式庫。
using msvc ; project user-config ; using boost : 1.39 : <include>C:/include/boost-1_39 <library>C:/lib ;
必須使用 using
規則來參考稱為 boost 的工具組。此工具組與您迄今為止所閱讀的工具組(如 msvc)不同:它不包含稍後將執行的任何程式。不過,由於預先建置的 Boost C++ 程式庫支援已在工具組中實作,因此必須使用 using
規則。
Boost C++ 程式庫的位置可能因其他程式庫而異。因此,將 using
規則納入兩個組態檔案之一有其道理。
可以傳遞參數給 using
規則:第一個是版本號碼,第二個是選項清單。在上面的 Jamfile 中,使用位於做為選項傳遞的目錄中的 Boost C++ 程式庫 1.39。
使用 boost 工具組後,就可以使用 Boost C++ 程式庫,而不需要自行定義目標。
import boost ; boost.use-project 1.39 ; exe hello : hello.cpp : <library>/boost//thread ;
如果某個程式使用 Boost C++ 程式庫,它可以參考稱為 boost 的專案中的目標。但為了識別專案,必須先匯入 boost 模組,並使用規則 boost.use-project
:匯入 boost 模組會讓 boost.use-project
規則可以使用。此規則預期會接收到版本號碼作為唯一引數。由於可以使用 using
規則來參考各種版本的 Boost C++ 程式庫,因此專案可以指定它要使用的版本。在上面的 Jamfile 中,程式 hello 使用版本 1.39 的 Boost.Thread。
如果您使用 Boost.Build 管理專案,並建立 Jamfiles,您始終會使用規則。因此,您應該要知道有哪些規則存在,以及如何使用它們。下表會提供有關最重要的規則的概觀。
某些參數的後面會出現星號、加號或問號。星號表示可以任意多個值,加號表示至少必須有一個值,問號表示必須有零個或剛好一個值。
名稱 | 參數 | 說明 |
---|---|---|
alias | name : sources * : requirements * : default-build * : usage-requirements * | 透過新名稱參考來源或其他目標。 |
build-project | dir | 參考另一個目錄中的 Jamfile 來建置專案。 |
conditional | condition + : requirements * | 建立有條件需求,而不使用有條件屬性。 |
exe | name : sources * : requirements * : default-build * : usage-requirements * | 建立可執行檔。 |
explicit | target-names * | 讓目標明確。 |
glob | wildcards + : excludes * | 透過萬用字元參考目錄中的檔案。 |
glob-tree | wildcards + : excludes * | 透過萬用字元來參照目錄及所有子目錄中的檔案。 |
install | name-and-dir : 來源 * : 需求 * : 預設建置 * | 將檔案安裝到目錄中。 |
lib | 名稱 + : 來源 * : 需求 * : 預設建置 * : 使用需求 * | 建置一個函式庫。 |
project | id ? : 選項 * : * | 設定專案選項。 |
unit-test | 目標 : 來源 : 屬性 * | 建置並執行可執行檔。 |
use-project | id : 位置 | 參照另一個目錄中的 Jamfile 以將專案識別碼作為目標。 |
using | 工具設定模組 : * | 選取工具組。 |
你的 Boost.Build 版本可能支援上面列出的更多規則。如果你想知道支援哪些規則,你應該查看 Boost.Build 安裝的 build
子目錄中的檔案。
使用功能可讓您指定確切建置二進位檔的方式。由於有許多可用組態選項,因此功能清單很長。下表介紹最重要的功能。
名稱 | 值 | 說明 |
---|---|---|
<address-model> | 16, 32, 64, 32_64 | 產生 16、32 或 64 位元程式碼。 |
<architecture> | x86、ia64、sparc、power、mips1、mips2、mips3、mips4、mips32、mips32r2、mips64、parisc、arm、combined、combined-x86-power | 設定處理器系列以產生程式碼。 |
<c++-template-depth> | 1, 2, 3, ... | 設定最大範本深度。 |
<cflags> | ... | 將旗標傳遞給 C 編譯器。 |
<cxxflags> | ... | 將旗標傳遞給 C++ 編譯器 |
<debug-symbols> | on、off | 建立偵錯符號。 |
<def-file> | ... | 設定 def 檔案路徑(特定於 Windows DLL)。 |
<define> | ... | 定義前處理指令。 |
<embed-manifest> | on、off | 內嵌清單(特定於 msvc 工具組)。 |
<host-os> | aix、bsd、cygwin、darwin、freebsd、hpux、iphone、linux、netbsd、openbsd、osf、qnx、qnxnto、sgi、solaris、unix、unixware、windows | 在有條件屬性中使用,如果功能取決於主機作業系統則使用。 |
<include> | ... | 設定包含目錄。 |
<inlining> | off、on、full | 內聯函數。 |
<library> | ... | 連結到函式庫(於 project 規則中使用)。 |
<link> | shared、static | 連結到函式庫的共用或靜態版本。 |
<linkflags> | ... | 將旗標傳遞給連結器。 |
<location> | ... | 設定目錄(用於 <code class="code">install</code> 指令) |
<name> | ... | 設定函式庫的基本檔名(用於 <code class="code">lib</code> 指令) |
<optimization> | off、speed、space | 產生最佳化的程式碼 |
<profiling> | off、on | 產生配檔程式碼 |
<runtime-link> | shared、static | 連結到單執行緒或執行緒安全執行時期函式庫 |
<search> | ... | 設定搜尋函式庫的目錄(與 <code class="code"><name></code> 一起用於 <code class="code">lib</code> 指令) |
<source> | ... | 在 <code class="code">project</code> 指令的 requirement 參數或條件屬性中設定來源 |
<target-os> | aix、appletv、bsd、cygwin、darwin、freebsd、hpux、iphone、linux、netbsd、openbsd、osf、qnx、qnxnto、sgi、solaris、unix、unixware、windows | 如果功能會受到目標作業系統影響,則用於條件屬性中 |
<threading> | single、multi | 建立單執行緒或執行緒安全版本 |
<toolset> | gcc、msvc、intel-linux、intel-win、acc、borland、como-linux、cw、dmc、hp_cxx、sun | 如果功能會受到工具集影響,則用於條件屬性中 |
<undef> | ... | 取消定義預處理器指令 |
<use> | ... | 只採用已參考目標的用法需求,但其他動作都不要執行 |
<variant> | debug、release、profile | 建立除錯、釋放或剖析版本 |
<warnings> | on、all、off | 關閉警告 |
<warnings-as-errors> | off、on | 將警告當作錯誤處理 |
要取得 Boost.Build 功能的完整且最新參考,請參閱 Boost.Build 安裝的工具子目錄中的 <code class="filename">builtin.jam</code> 檔案。針對從 <code class="code">feature.feature</code> 開始的行進行搜尋 - 這是用於定義功能的內部指令。
版權所有Boris Schäling 2009。根據 Boost 軟體授權版本 1.0 進行散布。(請參閱附檔的 LICENSE_1_0.txt 檔案,或以副本的方式取得:https://boost.dev.org.tw/LICENSE_1_0.txt)