子專案

有些平台不提供原生的封裝系統。在這些情況下,通常會將所有第三方函式庫捆綁在您的原始碼樹中。這通常不被提倡,因為這使得很難將這些類型的專案添加到例如禁止捆綁函式庫的 Linux 發行版中。

Meson 試圖通過使其非常容易同時提供這兩者來解決這個問題。實現這一點的方式是,Meson 允許您取得任何其他 Meson 專案,並使其成為您建置的一部分,而 (在最好的情況下) 不對其 Meson 設定進行任何變更。它將成為專案的一個透明部分。

應該注意的是,這僅保證適用於使用 Meson 建置的子專案。原因很簡單,因為沒有任何可能的方法可以可靠地使用混合建置系統來做到這一點。因此,此處僅描述 Meson 子專案。基於 CMake 的子專案也受到支援,但不保證可以運作。

子專案範例

通常,相依性包含一些標頭檔以及一個要連結的函式庫。要宣告這個內部相依性,請使用 declare_dependency 函數。

例如,假設我們有一個提供共享函式庫的簡單專案。它的 meson.build 看起來會像這樣。

project('libsimple', 'c')

inc = include_directories('include')
libsimple = shared_library('simple',
  'simple.c',
  include_directories : inc,
  install : true)

libsimple_dep = declare_dependency(include_directories : inc,
  link_with : libsimple)

相依性變數的命名慣例

理想情況下,相依性變數名稱應採用 <專案名稱>_dep 的形式。這樣一來,您就可以直接使用它,而無需查看該子專案的建置定義。

在需要宣告多個相依性的情況下,預設的相依性應命名為 <專案名稱>_dep (例如 gtest_dep),其他相依性則可以採用 <專案名稱>_<其他>_<名稱>_dep 的形式 (例如 gtest_main_dep - 具有 main 函數的 gtest)。

在應該應用常識的情況下,這些規則可能會有一些例外。

將變數新增到相依性

0.54.0 版新增

在某些情況下,專案可能會透過 pkg-config 或 cmake 定義呼叫者需要知道的特殊變數。Meson 提供了一個 dependency.get_variable 方法來隱藏所提供的相依性類型,並且這也適用於子專案。使用 variables 關鍵字新增字串字典

my_dep = declare_dependency(..., variables : {'var': 'value', 'number': '3'})

另一個專案可以透過以下方式存取

var = my_dep.get_variable(internal : 'var', cmake : 'CMAKE_VAR')

字典的值必須是字串,因為 pkg-config 和 cmake 會以字串形式傳回變數。

子專案中的建置選項

子專案的所有 Meson 功能 (例如專案選項) 都可以繼續運作,並且可以在主專案中設定。有一些限制,其中最重要的是全域編譯器引數必須在呼叫子專案之前在主專案中設定。子專案不得設定全域引數,因為沒有辦法在多個子專案之間可靠地執行此操作。要檢查您是否以子專案的形式執行,請使用 meson.is_subproject()

使用子專案

所有子專案都必須位於 subprojects 目錄中。subprojects 目錄必須位於專案的最上層。子專案宣告必須位於您最上層的 meson.build 中。

一個簡單的範例

讓我們使用 libsimple 作為子專案。

在專案的最上層建立 subprojects 目錄。然後將 libsimple 複製到 subprojects 目錄中。

您專案的 meson.build 應該看起來像這樣。

project('my_project', 'cpp')

libsimple_proj = subproject('libsimple')
libsimple_dep = libsimple_proj.get_variable('libsimple_dep')

executable('my_project',
  'my_project.cpp',
  dependencies : libsimple_dep,
  install : true)

請注意,子專案物件不會用作相依性,而是您需要使用 get_variable 從中取得宣告的相依性,因為一個子專案可能有多個宣告的相依性。

在系統函式庫和嵌入式來源之間切換

建置發行版套件時,不嵌入任何來源非常重要。有些發行版有禁止嵌入相依性的規則,因此您的專案必須在沒有這些相依性的情況下可建置,否則封裝者會討厭您。

以下說明如何使用系統函式庫,如果相依性不可用,則會退回到嵌入來源。

project('my_project', 'cpp')

libsimple_dep = dependency('libsimple', required : false)

if not libsimple_dep.found()
  libsimple_proj = subproject('libsimple')
  libsimple_dep = libsimple_proj.get_variable('libsimple_dep')
endif

executable('my_project',
  'my_project.cpp',
  dependencies : libsimple_dep,
  install : true)

由於這是一個非常常見的操作,因此 Meson 為此使用案例提供了一個捷徑。

dep = dependency('foo', fallback : ['subproject_name', 'variable_name'])

fallback 關鍵字引數接受兩個項目,子專案的名稱和保存相依性的變數名稱。如果您需要執行更複雜的操作,例如提取幾個不同的變數,則需要使用上述手動方法自行執行。

使用此捷徑,建置定義將如下所示。

project('my_project', 'cpp')

libsimple_dep = dependency('libsimple', fallback : ['libsimple', 'libsimple_dep'])

executable('my_project',
  'my_project.cpp',
  dependencies : libsimple_dep,
  install : true)

您可以通過將關鍵字引數添加到呼叫中來變更子專案的預設選項。例如,要變更預設的函式庫類型

libsimple_dep = dependency(
  'libsimple',
  fallback : ['libsimple', 'libsimple_dep'],
  default_options: ['default_library=static']
)

使用此設定,當系統提供 libsimple 時,我們將使用它並完全忽略子專案選項 (即,我們連結到共享系統函式庫)。如果不是這種情況,我們將使用嵌入式版本 (來自子專案的版本)。

請注意,libsimple_dep 可以指向外部或內部相依性,但您不必擔心它們之間的差異。Meson 會為您處理細節。

相依於其他子專案的子專案

子專案可以使用其他子專案,但所有子專案都必須位於最上層的 subprojects 目錄中。但是,不允許遞迴使用子專案,因此您不能讓使用子專案 b 的子專案 a,也讓 b 使用 a

取得子專案

Meson 隨附一個相依性系統,可自動取得相依性子專案。它在Wrap 相依性系統手冊中說明。

命令列選項

子專案的使用可以由使用者和發行版使用以下命令列選項進行控制

  • --wrap-mode=nodownload

    Meson 不會使用網路下載任何子專案或提取任何 wrap 資訊。只會使用現有的來源。當您只想使用軟體版本提供的來源,並想手動處理或提供遺失的相依性時,這非常有用 (主要適用於發行版)。

  • --wrap-mode=nofallback

    Meson 不會將子專案回退用於建置檔案中的任何相依性宣告,並且只會在系統中尋找它們。請注意,這不適用於無條件的 subproject() 呼叫,這些呼叫旨在用於系統無法提供的來源,例如 copylibs。

    此選項可以被 --force-fallback-for 特定相依性覆寫。

  • --wrap-mode=forcefallback

    Meson 不會查看系統是否有任何具有可用子專案回退的相依性,並且只會將子專案用於這些相依性。當您想測試回退設定,或特別想要針對子專案提供的函式庫來源進行建置時,這非常有用。

  • --force-fallback-for=list,of,dependencies

    Meson 不會查看系統是否有任何列於其中的相依性,前提是在宣告相依性時提供了回退。

    此選項優先於 --wrap-mode=nofallback,並且與 --wrap-mode=nodownload 結合使用時,僅在已下載相依性的情況下才有效。

    當您的專案有許多回退相依性時,但您只想針對其中一些相依性的函式庫來源進行建置時,這非常有用。

    警告:這可能會導致在同一個程序中混合使用同一個函式庫的系統版本和子專案版本。以下列案例為例

    • 您的系統上安裝了函式庫 glib-2.0gstreamer-1.0
    • gstreamer-1.0 相依於 glib-2.0,pkg-config 檔案 gstreamer-1.0.pc 具有 Requires: glib-2.0
    • 在您的應用程式建置定義中,您執行
      executable('app', ...,
        dependencies: [
          dependency('glib-2.0', fallback: 'glib'),
          dependency('gstreamer-1.0', fallback: 'gstreamer')],
      )
      
    • 您使用 --force-fallback-for=glib 進行設定。這會導致連結到兩個不同版本的函式庫 glib-2.0,因為 dependency('glib-2.0', fallback: 'glib') 將傳回子專案相依性,但是 dependency('gstreamer-1.0', fallback: 'gstreamer') 不會回退並傳回系統相依性,其中包括 glib-2.0 函式庫。為避免這種情況,每個本身相依於 glib-2.0 的相依性也必須強制回退,在本例中使用 --force-fallback-for=glib,gsteamer
  • --wrap-mode=nopromote

    自 0.56.0 起 Meson 會自動使用在子專案中找到的 wrap 檔案,並將它們複製到主專案中。可以透過傳遞 --wrap-mode=nopromote 來停用此新行為。在這種情況下,只會使用在主專案中找到的 wrap。

meson subprojects 命令

自 0.49.0 起

meson subprojects 有各種子命令來管理所有子專案。如果子命令在任何子專案上失敗,則執行會繼續執行其他子專案。所有子命令都接受指向主專案根原始碼目錄的 --sourcedir 引數。

自 0.56.0 起 所有子命令都接受 --types <file|git|hg|svn> 引數,以僅在給定類型的子專案上執行子命令。可以將多個類型設定為逗號分隔清單,例如 --types git,file

自 0.56.0 起 如果子命令在任何子專案上失敗,則會在最後傳回錯誤代碼,而不是傳回成功。

下載子專案

自 0.49.0 起

除非傳遞 --wrap-mode=nodownload 選項,否則 Meson 會在設定期間自動下載所需的子專案。有時最好事先下載所有子專案,以便可以離線執行 Meson 設定。命令列 meson subprojects download 可用於此目的,它會下載所有遺失的子專案,但不會更新已提取的子專案。

更新子專案

自 0.49.0 起

一旦擷取了子專案,Meson 不會自動更新它。例如,如果 wrap 檔案追蹤的是 git 分支,它將不會拉取最新的提交。

要一次拉取所有子專案的最新版本,只需執行命令:meson subprojects update

  • 如果 wrap 檔案來自 wrapdb,則下次 Meson 重新配置專案時,將會拉取並使用最新版本的 wrap 檔案。可以使用 meson --reconfigure 觸發此操作。先前的原始碼樹不會被刪除,以防止任何本地變更遺失。自 0.58.0 起,如果指定 --reset,則會刪除原始碼樹並提取新的原始碼。
  • 如果子專案目前處於分離模式,則會從 wrap 檔案中執行修訂版本的簽出。自 0.56.0 起,如果修訂版本已在本機存在但已過時,則也會執行變基。如果指定 --reset,則會執行硬重置而不是變基。
  • 如果子專案目前與 wrap 檔案指定的分支相同,則會對 origin 提交執行變基。自 0.56.0 起,如果指定 --reset,則會執行硬重置而不是變基。
  • 如果子專案目前與 wrap 檔案指定的分支不同,則會跳過它,除非傳遞了 --rebase 選項,在這種情況下,會對 origin 提交執行變基。自 0.56.0 起--rebase 參數已棄用且無效。取而代之的是,將執行從 wrap 檔案簽出修訂版本的操作,並且如果修訂版本已在本機存在但已過時,則也會執行變基。如果指定 --reset,則會執行硬重置而不是變基。
  • 自 0.56.0 起,如果 wrap 檔案中指定的 url 與 git 儲存庫的 origin 上設定的 URL 不同,則它不會更新,除非指定 --reset,在這種情況下,origin 的 URL 將會先重置。
  • 自 0.56.0 起,如果子專案目錄不是 git 儲存庫,但有 [wrap-git],則會忽略該子專案,除非指定 --reset,在這種情況下,會刪除該目錄並克隆新的儲存庫。

在所有 git 子專案中開始一個主題分支

自 0.49.0 起

命令列 meson subprojects checkout <branch_name> 將會在每個 git 子專案中簽出一個分支,或者使用 -b 參數建立一個分支。當在多個子專案中開始本地變更時,這非常有用。您仍然有責任在進行本地變更的每個儲存庫中提交和推送。

要回到 wrap 檔案中設定的修訂版本 (即 master),只需執行不帶分支名稱的 meson subprojects checkout 即可。

自 0.56.0 起,任何待處理的變更現在都會在簽出新分支之前暫存。

在所有子專案上執行命令

自 0.51.0 起

命令列 meson subprojects foreach <command> [...] 將會在每個子專案目錄中執行一個命令。例如,這對於在對子專案執行其他操作之前檢查子專案的狀態(例如,使用 git statusgit diff)非常有用。

為什麼所有子專案都必須在單個目錄中?

原因有幾個。

首先,為了維持任何形式的健全性,系統必須阻止使用 subdir() 或其變體進入其他子專案。將子專案放在定義良好的位置可以使這變得容易。如果子專案可以在任何地方,那將會困難得多。

其次,終端使用者能夠輕鬆查看任何專案具有哪些子專案至關重要。因為它們在一個且只有一個地方,所以檢視它們變得容易。

這也是一個慣例問題。由於所有 Meson 專案都具有相同的子專案佈局,因此在專案之間切換變得更容易。您不必花時間在新專案上在原始碼樹中尋找子專案。它們總是在相同的地方。

最後,如果可以在任何地方都有子專案,這會增加在原始碼樹中擁有許多不同(可能不相容)的依賴項版本的可能性。然後,更改某些程式碼(例如更改遍歷目錄的順序)可能會導致意外使用完全不同版本的子專案。

搜尋結果為