1 前言

本文是《Linux 包管理器生态漫谈》的续作。在前文中,我们拆解了 Linux 包管理器的各种设计理念;而本次,我们将目光转向这些工具背后的基础设施——软件仓库。

软件仓库对软件包的录入与存储通常有三种逻辑:

  • 源码录入: 仓库维护的是软件的构建说明。
  • 制品录入: 仓库直接存储编译好的二进制文件(如 .deb 或 .rpm)。
  • 混合模式: 二者兼有。

我今天要介绍的是“源码录入”逻辑中极具代表性的一员:Ports 模式。

这种模式的有趣之处在于:它虽然属于源码录入,但软件仓库中并不真正存储开源软件的源码包,而是只存储了一套构建脚本。在构建过程中,构建系统会实时根据脚本指令去互联网下载源码。

2 Ports 模式的特征

在 Ports 模式下,“软件”的本体并不是编好的二进制文件,而是那一套构建脚本。

这种“以脚本定义软件,以源码为本位”的设计,导致它在逻辑上跟传统的包管理系统完全不同:

  • 仓库里只存“配方”:软件源的维护者通过一个巨大的代码仓库来维护软件包。这个仓库里全是文本文件,它不存软件源码,只存“配方”:去哪下源码、下完怎么打补丁、用什么参数编译、装到哪个目录下。
  • 仓库本身就是“包管理器”:在很多模式下,软件仓库(存放数据)和客户端(执行逻辑)是分离的,比如 Debian 的 apt 仓库与 apt 包管理器就是两个互相独立的存在。但在 Ports 模式里,仓库里的公共脚本配合系统自带的 make,本身就已经构成了一套完善的包管理系统。
  • 二进制包只是“缓存”:虽然现在的 Ports 社区都会在源码库的基础上额外提供预编译的二进制包,能帮你省去从源码实时编译的繁琐动作,但这些包本质上只是官方替你跑了一遍脚本后的“缓存结果”。如果官方包不符合你的口味(比如你想精简功能),你完全可以无缝切换回源码模式,按自己的意愿重新编一遍。

3 Ports 模式的起源

3.1 最初的玩家

Ports 这种软件组织模式由 FreeBSD 首创。狭义上来讲,Ports 正是特指 FreeBSD 的 Ports Collection。

FreeBSD 官方对 port 的介绍是这样的:

FreeBSD port 是一组文件,旨在自动执行从源代码编译应用程序的过程。port 中包含了自动下载、提取、修补、编译和安装应用程序所需的所有信息。

3.2 FreeBSD 里面的 port

以 zsh 这个软件为例,它的 port 长这样

在这里插入图片描述

图上的所有文件和目录加起来,就叫做一个 port。

各个文件的作用是这样的:

  • Makefile:包含指定如何编译应用程序以及在何处安装其组件的语句。
  • distinfo:包含构建 port 必须下载的文件的名称和校验和。
  • files/:此目录包含程序在 FreeBSD 上编译和安装所需的所有补丁。此目录还可能包含用于构建 port 的其他文件。
  • pkg-descr:提供该程序的更详细描述。
  • pkg-plist:port 将会安装的所有文件的列表。它还会告诉 ports 系统在卸载时需要删除哪些文件。

图上的 zsh 只是一个示例,实际上所有 port 的文件结构都要符合这个规范。这个规范有一个专门的名字,叫做 ports skeleton。

我们打开 zsh 的 Makefile 看看,注意这两个变量:MASTER_SITESDISTFILES。将这两个变量进行拼接,就可以获取到源码包的下载地址了,zsh 的构建过程中就是通过这个地址去实时下载源码的。

在这里插入图片描述

很多个 port 放在一起,就叫做一个 Ports Collection,这是官方最正式的术语。由于它的目录结构层级分明,从顶层的分类目录一直延伸到具体的软件目录,所以开发者们也形象地称之为 Ports Tree。此外它还有另一个比较常用的简称,叫做 Ports。

Ports Collection 是一个很大的目录,里面按照类型把软件进行分类,同类的软件会放在一起。

在这里插入图片描述

比如 zsh 这个软件是属于“shells”这个类别,因此它的 port 就放在 shells/zsh 这个目录中。

在这里插入图片描述

3.3 如何使用 port

在 FreeBSD 上,我们要如何将一个 port 构建成一个软件呢?

首先,需要下载一份 Ports Collection,并把它放置到 /usr/ports 目录中

git clone https://git.FreeBSD.org/ports.git /usr/ports

下载完成之后,进入到 port 所在的目录,然后执行 make install 即可。比如我要编译安装 zsh,我就要这么做

cd /usr/ports/shells/zsh
make install

这个过程看似和我们平时编译 C 程序一样,但不同之处在于:当你执行 make install 时,由于该 port 引用了仓库内的的公共 Makefile(bsd.port.pre.mkbsd.port.post.mk),它会自动触发一套完整的流水线——下载源码、解压、打补丁、编译。

这就是 Ports 最原始的样子:仓库即工具。只要系统有 git 和 make,你不需要安装任何额外的包管理器客户端,就能完成软件全生命周期的管理。

3.4 从 Ports 到 Packages 的进化

纯粹的 Ports 模式虽然灵活,但有一个绕不开的痛点:极其耗时,因为所有软件包都需要实时编译。

为了解决这个问题,FreeBSD 引入了 Packages 方案,作为 Ports 方案的补充。

Packages 方案是这么做的:

  1. 官方批量构建:社区服务器定期拉取整个 Ports Collection,按照默认配置批量构建生成二进制包(Package)。
  2. 建立分发仓库:将这些构建好的制品托管在二进制仓库中。官方仓库主页:http://pkg.freebsd.org/
  3. 引入客户端工具:向用户提供一个叫做 pkg 的二进制包管理工具,用户通过这个工具即可便捷地下载安装这些二进制包。

比如我要直接从二进制安装 zsh,我只需要执行这样一条命令即可

pkg install zsh

4 Ports 模式的主要玩家

自 FreeBSD 首创提出 Ports 方案之后,后续出现了大量的效仿者,许多类 UNIX 系统就使用了这种方案作为软件仓库的组织模式。

虽然它们名称各不相同,具体的技术实现细节也有所差异(比如使用的构建脚本已经不局限于 Makefile 而是选择了其他的形态),但理念都是一样的,所以我这里统一也都把它们归类为 Ports 模式。

类别 OS 名称 Ports 仓库的名称 构建脚本 二进制预构建包 二进制下载器(命令)
BSD分支 FreeBSD Ports Collection Makefile pkg
BSD分支 NetBSD pkgsrc Makefile pkgin
BSD分支 OpenBSD ports tree Makefile pkg_add
BSD分支 DragonflyBSD DPorts Makefile pkg
Linux发行版 Gentoo Linux Gentoo Repository ebuild(bash) emerge
Linux发行版 Alpine Linux aports tree APKBUILD(bash) apk
Linux发行版 Arch Linux official repositories PKGBUILD(bash) pacman
Linux发行版 NixOS nixpkgs nix表达式 nix
其他 macOS homebrew-core(第三方) formula(ruby) brew
其他 SmartOS pkgsrc Makefile pkgin

5 pkgsrc 框架

上述的各种 Ports 方案,大部分都是有一定的讨论度的(尤其是那几个 Linux 发行版),所以我就没必要讲了,这次我想讲讲更冷门一些的 pkgsrc,它的高度可移植性令我十分感兴趣。

官方的介绍是这样的:

pkgsrc 是一个用于在类 UNIX 系统上管理第三方软件的框架,目前包含超过 26,000 个软件包。它是 NetBSD 和 SmartOS 的默认软件包管理器,可用于在大量其他类 UNIX 平台上轻松构建免费软件。pkgsrc 生成的二进制软件包无需从源代码编译任何内容即可使用。它可以轻松地补充现有系统上的软件。

pkgsrc 功能强大且可配置,支持为任意安装前缀构建软件包,允许多个分支在一台机器上共存,提供构建选项框架、编译器转换框架以及其他高级功能。此外,它还支持非特权使用和安装。

NetBSD 已经包含使用 pkgsrc 所需的工具;在其他平台上,您需要引导 pkgsrc 来安装包管理工具。

它最大的优点就是支持的系统极广。官方称其支持 20 种类 UNIX 系统和 15 种 CPU 架构。

图片描述

从它的文档中可以看出支持的平台列表

我们平时常见的 OS 基本都包括在内了,包括 Linux、macOS 以及四大 BSD 都得到了支持,甚至连 windows 上的 Cygwin 环境都能支持。

它的工作原理和使用方法,整体上和前面讲的 FreeBSD Ports 差不多,因为它本身就是从 FreeBSD Ports 这套系统 fork 出来进行演进的,可以认为它是 FreeBSD Ports 的“跨平台进化版”。

它的可移植性高,主要是出于以下原因:

  • 逻辑脚本化:包管理逻辑主要靠 Makefile 实现,而纯文本脚本天然比二进制代码更容易跨平台适配。
  • 工具链去耦合:它自带的一套构建工具和管理工具(bmake、pkg_install 等)设计得非常精简,具备较高的可移植性,不依赖特殊的 libc 接口,因此能在众多类 UNIX 系统上编译运行。

但需要强调一点:pkgsrc 框架本身的可移植性高,并不意味着仓库里那两万多个软件的可移植性也高。

比如 这里 有一篇邮件,里面清清楚楚地提到了这一点

But, yes, `supported platform’ does not mean that all packages will succeed
to be built on Cygwin. It’s a chance for you to fix the issue 😃

因此,即使 pkgsrc 支持了你使用的平台,你也仍可能会在安装和使用软件包的过程中遇到错误。如果你有能力修复它,这就是一个为社区做贡献的机会了。

6 参考链接

  1. Chapter 4. Installing Applications: Packages and Ports
  2. pkgsrc: The NetBSD Packages Collection
  3. OpenBSD Ports - Working with Ports
  4. DragonFlyBSD: DPortsUsage
  5. Portage - Gentoo Wiki
  6. Aports tree - Alpine Linux
  7. Category:Package management - ArchWiki
  8. NixOS/nixpkgs: Nix Packages collection & NixOS
  9. Homebrew — The Missing Package Manager for macOS (or Linux)
  10. Managing Packages - SmartOS Documentation
  11. pkgsrc
  12. 如何在 Linux 上使用 pkgsrc
  13. Pkgsrc on cygwin
  14. 几个使用类似 BSD ports 软件包管理的 Linux 发行版
  15. 如何把NetBSD系统的软件管理工具pkgsrc移植到龙架构,编译详情步骤
Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐