子项目

某些平台没有提供原生的打包系统。在这些情况下,通常将所有第三方库捆绑到源代码树中。这通常不被推荐,因为它使得将这类项目添加到例如那些禁止捆绑库的 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)

依赖项变量的命名约定

理想情况下,依赖项变量名应该是 <project_name>_dep 的形式。这样,您就可以直接使用它,甚至无需查看该子项目的构建定义。

在需要声明多个依赖项的情况下,默认依赖项应命名为 <project_name>_dep(例如 gtest_dep),其他依赖项可以采用 <project_name>_<other>_<name>_dep 的形式(例如 gtest_main_dep - 带有主函数的 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 目录中。但是,不允许递归使用子项目,因此您不能让子项目 a 使用子项目 b,而 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 起

Meson 会在配置期间自动下载所需的子项目,除非传递了 --wrap-mode=nodownload 选项。有时,最好提前下载所有子项目,以便可以脱机执行 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 项目在子项目方面具有相同的布局,因此在项目之间切换变得更容易。您不必在新的项目中花费时间在源代码树中来回查找子项目。它们始终位于同一个位置。

最后,如果您可以在任何地方放置子项目,那么这将增加在您的源代码树中拥有许多不同(可能不兼容)版本的依赖项的可能性。然后,更改一些代码(例如更改遍历目录的顺序)可能会意外地导致使用完全不同的版本的子项目。

搜索结果是