封裝最佳實務與提示

在為專案撰寫 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_dependencysources 引數的主要用途是為後端建構正確的相依性圖,如上一節所示。非常重要的是要注意,它不應用於直接公開相依性的可編譯原始碼(.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 是中間連結目標。但是,對於大多數程式庫來說,此規則不適用,因為您通常無法控制其他人如何使用您的程式庫,因此不應公開原始碼。

搜尋結果是