封裝最佳實務與提示
在為專案撰寫 Meson 建置定義時,需要考慮幾個事項。當專案將作為子專案使用時,尤其如此。此頁面列出撰寫定義時要考慮的幾件事。
不要將 config.h 放入外部搜尋路徑
許多專案使用 config.h
標頭檔來配置其專案內部。這些檔案永遠不會安裝到系統標頭檔中,因此不會發生包含衝突。子專案的情況並非如此,您的專案樹狀結構可能具有任意數量的組態檔案,因此我們需要確保它們不會衝突。
基本問題是子專案的使用者必須能夠包含子專案標頭,而不會看到其 config.h
檔案。最正確的解決方案是將 config.h
檔案重新命名為唯一的名稱,例如 foobar-config.h
。除非您是相關子專案的維護者,否則這通常是不可行的。
務實的解決方案是將 config 標頭放在沒有其他標頭檔的目錄中,然後將其隱藏起來,不讓其他人看到。一種方法是建立一個名為 internal
的頂層子目錄,並使用它來建置您自己的原始碼,如下所示
subdir('internal') # create config.h in this subdir
internal_inc = include_directories('internal')
shared_library('foo', 'foo.c', include_directories : internal_inc)
許多專案將他們的 config.h
保存在頂層目錄中,該目錄中沒有其他原始碼檔案。在這種情況下,您不需要移動它,而是可以這樣做
internal_inc = include_directories('.') # At top level meson.build
使程式庫可以建置為靜態和共享
某些平台(例如 iOS)要求將所有內容靜態連結到您的主要應用程式中。在其他情況下,您可能需要共享程式庫。由於 Meson 的重新連結最佳化,它們在開發過程中也更快。但是,在所有建置中建置兩種程式庫類型既緩慢又浪費。
您的專案應使用 library
方法,該方法可以使用 default_library
內建選項在共享和靜態之間切換。
mylib = library('foo', 'foo.c')
明確宣告產生的標頭
Meson 的 Ninja 後端與 Make 和其他系統的工作方式不同。它不是逐目錄處理,而是查看整個建置定義,並以對外部看起來像是隨機順序的方式執行單個編譯作業。
這樣做的原因是這樣效率更高,因此您的建置完成速度更快。缺點是您必須小心處理您的相依性。這裡最常見的問題是使用程式碼產生器等在編譯時產生的標頭。如果在建置使用這些程式庫的程式碼時需要這些標頭,則編譯作業可能會在程式碼產生步驟之前執行。解決方法是像這樣明確相依性
myheader = custom_target(...)
mylibrary = shared_library(...)
mydep = declare_dependency(link_with : mylibrary,
include_directories : include_directories(...),
sources : myheader)
然後您可以像往常一樣使用相依性
executable('dep_using_exe', 'main.c',
dependencies : mydep)
Meson 將確保標頭檔已在編譯 main.c
之前建置。
避免在 declare_dependency 中公開可編譯的原始碼檔案
declare_dependency
中 sources
引數的主要用途是為後端建構正確的相依性圖,如上一節所示。非常重要的是要注意,它不應不用於直接公開相依性的可編譯原始碼(.c
、.cpp
等),而應僅用於標頭/組態檔案。以下範例將說明如果您不小心公開可編譯的原始碼檔案,可能會出什麼問題。
因此,您已經閱讀了統一建置以及 Meson 如何原生支援它們。您決定公開相依性的原始碼,以便擁有包含其相依性的統一建置。對於您的支援程式庫,您可以執行
my_support_sources = files(...)
mysupportlib = shared_library(
...
sources : my_support_sources,
...)
mysupportlib_dep = declare_dependency(
...
link_with : mylibrary,
sources : my_support_sources,
...)
對於您的主要專案,您可以執行
mylibrary = shared_library(
...
dependencies : mysupportlib_dep,
...)
myexe = executable(
...
link_with : mylibrary,
dependencies : mysupportlib_dep,
...)
這非常危險。在建置時,mylibrary
將建置並將支援原始碼 my_support_sources
連結到產生的共享程式庫中。然後,對於 myexe
,這些相同的支援原始碼將再次編譯,將連結到產生的可執行檔中,此外它們已存在於 mylibrary
中。這可能會快速違反 C++ 中的一個定義規則 (ODR),因為您有多個符號的定義,從而產生未定義的行為。雖然 C 沒有嚴格的 ODR 規則,但標準中沒有任何語言保證這種行為有效。違反 ODR 可能會導致奇怪的特異性故障,例如區段錯誤。在絕大多數情況下,透過 declare_dependency
中的 sources
引數公開程式庫原始碼是不正確的。如果您希望獲得完整的跨程式庫效能,請考慮將 mysupportlib
建置為靜態程式庫,並採用 LTO。
這個規則也有例外。如果對程式庫的使用方式有一些自然約束,則可以公開原始碼。例如,GoogleTest 的 WrapDB 模組直接公開 GTest 和 GMock 的原始碼。這是有效的,因為 GTest 和 GMock 只會用於終端連結目標。終端目標是相依性連結鏈中的最終目標,例如上一個範例中的 myexe
,而 mylibrary
是中間連結目標。但是,對於大多數程式庫來說,此規則不適用,因為您通常無法控制其他人如何使用您的程式庫,因此不應公開原始碼。
搜尋結果是