Wrap 依赖系统手册

多平台开发的主要问题之一是管理所有依赖项。这在许多平台上都很麻烦,尤其是在没有内置包管理器的平台上。后一个问题已通过第三方包管理器来解决。它们并不是针对最终用户部署的解决方案,因为你无法告诉他们安装一个包管理器来使用你的应用程序。在这些平台上,你必须生成自包含应用程序。同样适用于目标平台缺少(最新版本的)应用程序的依赖项的情况。

传统的方法是将依赖项捆绑在自己的项目中。要么作为预构建的库和头文件,要么将源代码嵌入到源代码树中,并重写构建系统,将其作为项目的一部分进行构建。

这既繁琐又容易出错,因为始终都是手动完成的。Meson 的 Wrap 依赖系统旨在提供一种自动化的方式来完成此操作。

工作原理

Meson 有一个名为子项目的概念。它们是将一个 Meson 项目嵌套在另一个项目中的方式。任何使用 Meson 构建的项目都可以检测到它被构建为子项目,并以一种易于使用的方式构建自身(通常这意味着作为静态库)。

要将此类项目用作依赖项,你只需将它复制并提取到项目subprojects目录中即可。

但是,有一个更简单的方法。你可以指定一个 Wrap 文件,它会告诉 Meson 如何为你下载它。如果你在构建中使用这个子项目,Meson 会在构建期间自动下载并提取它。这使得子项目嵌入变得非常容易。

所有 wrap 文件都必须具有<project_name>.wrap形式的名称,并且必须位于subprojects目录中。

目前 Meson 有四种类型的 wrap

  • wrap-file
  • wrap-git
  • wrap-hg
  • wrap-svn

wrap 格式

Wrap 文件以 ini 格式编写,单个标题包含 wrap 的类型,后跟描述如何获取源代码、验证它们以及在需要时修改它们的属性。名为libfoobar的 wrap 的示例 wrap-file 将具有文件名libfoobar.wrap,并且看起来像这样

[wrap-file]
directory = libfoobar-1.0

source_url = https://example.com/foobar-1.0.tar.gz
source_filename = foobar-1.0.tar.gz
source_hash = 5ebeea0dfb75d090ea0e7ff84799b2a7a1550db3fe61eb5f6f61c2e971e57663

wrap-git 的示例将如下所示

[wrap-git]
url = https://github.com/libfoobar/libfoobar.git
revision = head
depth = 1

wrap 接受的配置属性

  • directory - 子项目根目录的名称,默认为 wrap 的名称。

0.55.0 开始,这些可以在所有 wrap 类型中使用,之前它们被保留给 wrap-file

  • patch_url - 用于检索可选覆盖存档的下载 URL
  • patch_fallback_url - 从 patch_url 下载失败时使用的备用 URL 自:0.55.0
  • patch_filename - 下载的覆盖存档的文件名
  • patch_hash - 下载的覆盖存档的 sha256 校验和
  • patch_directory - 自 0.55.0 覆盖目录,当文件是本地的而不是下载的存档时,它是 patch_filename 的替代方案。该目录必须放在 subprojects/packagefiles 中。
  • diff_files - 自 0.63.0 本地 diff 文件的逗号分隔列表(参见下面的Diff 文件)。
  • method - 自 1.3.0 此子项目使用的构建系统。默认为 meson。支持的方法

特定于 wrap-file

  • source_url - 用于检索 wrap-file 源代码存档的下载 URL
  • source_fallback_url - 从 source_url 下载失败时使用的备用 URL 自:0.55.0
  • source_filename - 下载的源代码存档的文件名
  • source_hash - 下载的源代码存档的 sha256 校验和
  • lead_directory_missing - 对于 wrap-file,创建前导目录名称。当源文件没有前导目录时需要。

0.55.0 开始,可以只在 .wrap 文件中使用 source_filenamepatch_filename 值(不使用 source_urlpatch_url)来指定 subprojects/packagefiles 目录中的本地存档。当使用这种方法时,*_hash 条目是可选的。此方法应优先于下面描述的旧的 packagecache 方法。

0.49.0 开始,如果在项目的 subprojects/packagecache 目录中找到了 source_filenamepatch_filename,即使 --wrap-mode 选项设置为 nodownload,它也会被使用,而不是下载文件。将检查文件的哈希值。

1.3.0 开始,如果设置了 MESON_PACKAGE_CACHE_DIR 环境变量,则使用它代替项目的 subprojects/packagecache。这允许在多个项目之间共享缓存。此外,它可以包含一个已经解压的源代码树,只要它具有与 wrap 文件中的 directory 字段相同的目录名称。在这种情况下,该目录将在应用补丁之前复制到 subprojects/ 中。

特定于基于 VCS 的 wrap

  • url - 要克隆的 wrap-git 存储库的名称。必需的。
  • revision - 要签出的修订版的名称。必须是:VCS 的 checkout 命令的有效值(例如 git 标签),或者(对于 git)head 以跟踪上游的默认分支。必需的。

特定于 wrap-git

  • depth - 浅层克隆存储库到 X 个提交次数。这可以节省带宽和磁盘空间,并且通常应始终指定,除非需要提交历史记录。请注意,git 始终允许浅层克隆分支,但为了浅层克隆提交 ID,服务器必须支持 uploadpack.allowReachableSHA1InWant=true(自 0.52.0)
  • push-url - 作为 git push-url 配置的备用 URL。如果子项目将被开发并更改推送到上游,这将很有用。(自 0.37.0)
  • clone-recursive - 也克隆存储库的子模块 (自 0.48.0)

带有 Meson 构建补丁的 wrap-file

不幸的是,世界上大多数软件项目都没有使用 Meson 构建。因此,Meson 允许你指定一个补丁 URL。

出于历史原因,这被称为“补丁”,但它用作覆盖层来添加或替换文件,而不是修改它们。该文件必须是存档;它将被下载并自动提取到子项目中。提取的文件将包括给定子项目的 Meson 构建定义。

这种方法使嵌入需要构建系统更改的依赖项变得非常简单。你可以在完全隔离的情况下为依赖项编写 Meson 构建定义。这比在自己的源代码树中进行操作要好得多,尤其是在它包含数十万行代码的情况下。一旦你拥有了一个有效的构建定义,只需将 Meson 构建文件(以及你更改的其他文件)压缩并将其放到你可以下载它们的地方。

0.55.0 之前,Meson 构建补丁只支持 wrap-file 模式。当使用 wrap-git 时,存储库必须包含所有 Meson 构建定义。从 0.55.0 开始,Meson 构建补丁支持任何 wrap 模式,包括 wrap-git。

Diff 文件

自:0.63.0

你也可以提供 diff 格式的本地补丁文件。出于历史原因,它们被称为“diff 文件”,因为“patch”名称已经用于覆盖存档。

diff 文件由 diff_files 属性(逗号分隔列表)描述,并且必须在 subprojects/packagefiles 目录中本地可用。

Meson 将在提取或克隆项目后以及应用覆盖存档(patch_*)后应用 diff 文件。对于此功能,必须可以使用 patchgit 命令行工具。

diff 文件将使用 -p1 应用,即,将第一个路径组件视为要剥离的前缀。这是由 Git 生成的 diff 的默认值。

[wrap-file]
directory = libfoobar-1.0

source_url = https://example.com/foobar-1.0.tar.gz
source_filename = foobar-1.0.tar.gz
source_hash = 5ebeea0dfb75d090ea0e7ff84799b2a7a1550db3fe61eb5f6f61c2e971e57663

diff_files = libfoobar-1.0/0001.patch, libfoobar-1.0/0002.patch

provide 部分

*自 0.55.0

Wrap 文件可以在 [provide] 部分中定义它提供的依赖项。

[provide]
dependency_names = foo-1.0

当一个 wrap 文件提供依赖项 foo-1.0 时,如上所示,任何对 dependency('foo-1.0') 的调用都会自动回退到该子项目,即使没有提供 fallback 关键字参数。名为 foo.wrap 的 wrap 文件会隐式提供依赖项名称 foo,即使 [provide] 部分不存在也是如此。

可选依赖项,如 dependency('foo-1.0', required: get_option('foo_opt')),其中 foo_opt 是一个设置为 auto 的功能选项,不会回退到 wrap 文件中定义的子项目,原因有两个

  • 它允许首先以其他方式查找依赖项,例如使用 cc.find_library('foo'),只有在失败时才会回退。
# this won't use fallback defined in foo.wrap
foo_dep = dependency('foo-1.0', required: false)
if not foo_dep.found()
  foo_dep = cc.find_library('foo', has_headers: 'foo.h', required: false)
  if not foo_dep.found()
    # This will use the fallback
    foo_dep = dependency('foo-1.0')
    # or
    foo_dep = dependency('foo-1.0', required: false, fallback: 'foo')
  endif
endif
  • 有时,当未显式请求该功能时,未找到的依赖项比回退更可取。在这种情况下,dependency('foo-1.0', required: get_option('foo_opt')) 只有在用户将 foo_opt 设置为 enabled 而不是 auto 时才会回退。自 0.58.0 开始,如果 wrap_mode 设置为 forcefallbackforce_fallback_for 包含子项目,则上述可选依赖项将回退到 wrap 文件中定义的子项目。

如果希望对可选依赖项进行回退,则必须显式传递 fallbackallow_fallback 关键字参数。自 0.56.0 开始,dependency('foo-1.0', required: get_option('foo_opt'), allow_fallback: true) 即使 foo_opt 设置为 auto,也会使用回退。在 0.55.0 版本上,可以使用 dependency('foo-1.0', required: get_option('foo_opt'), fallback: 'foo') 实现相同的效果。

此机制假设子项目调用 meson.override_dependency('foo-1.0', foo_dep),以便 Meson 知道应使用哪个依赖项对象作为回退。由于该方法是在 0.54.0 版本中引入的,因此作为对尚未使用该方法的项目的过渡帮助,可以在 wrap 文件中以 foo-1.0 = foo_dep 格式的条目提供变量名称。

例如,当使用足够新的 glib 版本时,它使用 meson.override_dependency() 覆盖 glib-2.0gobject-2.0gio-2.0,wrap 文件将如下所示

[wrap-git]
url=https://gitlab.gnome.org/GNOME/glib.git
revision=glib-2-62
depth=1

[provide]
dependency_names = glib-2.0, gobject-2.0, gio-2.0

对于旧版本的 glib,需要指定依赖项变量名

[wrap-git]
url=https://gitlab.gnome.org/GNOME/glib.git
revision=glib-2-62
depth=1

[provide]
glib-2.0=glib_dep
gobject-2.0=gobject_dep
gio-2.0=gio_dep

程序也可以由 wrap 文件提供,使用 program_names

[provide]
program_names = myprog, otherprog

使用这样的 wrap 文件,find_program('myprog') 会自动回退以使用子项目,假设它使用 meson.override_find_program('myprog')

CMake wrap

注意:这是实验性的,没有向后或向前兼容性保证。请参阅Meson 关于混合构建系统的规则

由于 CMake 模块不知道提供的依赖项的公共名称,因此 CMake .wrap 文件不能使用 dependency_names = foo 语法。相反,应该使用 dep_name = <target_name>_dep 语法,其中 <target_name> 是 CMake 库的名称,所有非字母数字字符都被下划线 _ 替换。

例如,一个 CMake 项目,它的 CMakeList.txt 中包含 add_library(foo-bar ...),并且应用程序通常使用依赖项名称 foo-bar-1.0(例如,通过 pkg-config)来找到它,则该项目将拥有一个像这样的 wrap 文件

[wrap-file]
...
method = cmake
[provide]
foo-bar-1.0 = foo_bar_dep

Cargo 包裹

注意:这是实验性的,没有向后或向前兼容性保证。请参阅Meson 关于混合构建系统的规则

Cargo 子项目会自动覆盖 <package_name>-<version>-rs 依赖项名称

  • package_nameCargo.toml 文件的 [package] name = ... 部分定义。
  • version 是从 [package] version = ... 推断出来的 API 版本,如下所示
    • x.y.z -> 'x'
    • 0.x.y -> '0.x'
    • 0.0.x -> '0' 这允许为同一 crate 的不兼容版本创建不同的依赖项。
  • -rs 后缀用于与常规系统依赖项区分开来,例如 gstreamer-1.0 是系统 pkg-config 依赖项,而 gstreamer-0.22-rs 是 Cargo 依赖项。

这意味着当 Cargo.toml 中的包名为 foo,版本为 1.2 时,.wrap 文件的 [provide] 部分应该包含 dependency_names = foo-1-rs

请注意,版本组件是在 Meson 1.4 中添加的,以前的版本使用 <package_name>-rs 格式。

Cargo 子项目需要一个 toml 解析器。Python >= 3.11 内置了一个解析器,较旧的 Python 版本需要外部的 tomli 模块或 toml2json 程序。

例如,一个包名为 foo-bar 的 Cargo 项目将拥有这样的 wrap 文件

[wrap-file]
...
method = cargo
[provide]
dependency_names = foo-bar-0.1-rs

此外,如果 meson/meson.build 文件存在,Meson 将调用 subdir('meson'),该项目可以在其中添加通常属于 build.rs 的手动逻辑。需要遵守一些命名约定

  • extra_args 变量是预定义的,可以用来添加任何 Rust 参数。通常用作 extra_args += ['--cfg', 'foo']
  • extra_deps 变量是预定义的,可以用来添加额外的依赖项。通常用作 extra_deps += dependency('foo')

从 *1.5.0* 开始,Cargo 包裹也可以在 (子)项目源代码树的根目录中提供 Cargo.lock 文件。Meson 将自动加载该文件并将其转换为一系列包裹定义。

使用包裹项目

包裹提供了一种便捷的方式,将项目获取到子项目目录中。然后,您可以像使用常规子项目一样使用它(参见 子项目)。

获取包裹

通常情况下,您不需要手动编写包裹。

有一个名为 WrapDB 的在线仓库,提供许多可供使用的依赖项。您可以在这里阅读有关 WrapDB 的更多信息 这里.

Meson 还提供了一个子命令来获取和管理包裹(参见 使用 wraptool)。

搜索结果是