前言

使用 Arch, 或者基于 Arch 的 Manjaro 时,有些软件官方仓库里没有,而网上又可以下载到编译好的二进制文件,deb包,或者rpm包。二进制文件可以直接运行,deb或者rpm包解压后,找到对应的二进制文件,也可以直接运行。

但时,它们不被 pacman 包管理器管理,也不能从 app lancher 里面便捷的运行。

对于 deb 包,AUR库里有一个 debtap 脚本,可以将 deb 包转换成 pacman -U 可以直接安装的 .pkg.tar.zst 格式。这个脚本非常方便,但是由于这个脚本考虑非常全面,注意普适性,这就导致有时候转换后的 .pkg.tar.zst 包会出现一些问题,安装不上。

makepkg 打包软件是非常简单的事情,只是如果一点也不了解的话,会生怵。但是只要知道了它的运行机理,就会觉得非常简单,而且很多问题也就不存在了。

makepkg 简介

详细地介绍在 ArchWiki 里面都有介绍。在此按我的理解梳理一下。

makepkg 是 Arch 系统中的一个软件,它的作用是将写好的程序源代码编译好,并且打包成 .pkg.tar.zst 格式,然后发布。大家就可以下载打包好的 .pkg.tar.zst 文件在 Arch 上直接安装。

要想用 makepkg 打包一个程序,必须先写一个 PKGBUILD 文件。系统中有一些示例文件,比如 /usr/share/pacman/PKGBUILD.proto

写好了 PKGBUILD 文件,就可以直接在 PKGBUILD 文件所在的目录直接运行

makepkg

然后就生成了 .pkg.tar.zst 格式的软件包,然后 pacman -U 安装就可以了。

一个最简单的 PKGBUILD 文件

PKGBUILD 文件使用的是 shell 语言。最简单的 PKGBUILD 文件如下

pkgname=hello-makepkg
pkgver=1.0
pkgrel=1
arch=('x86_64')

package() {
    echo "packaging..."
}

前四行定义了四个变量,是软件的名字,版本,以及运行架构。最后三行定义了一个函数,输出一行字 packaging...。

pkgnamepkgverpkgrelarch 四个变量必须定义。名为 package() 的函数会被 makepkg 自动执行。

这个最简单的 PKGBUILD 文件是 trivial 的,因为它什么也没干,只是走了个过场。

打包流程(一个不 trivial 的测试)

下面写一个不 trivial 的 PKGBUILD 文件。如下:

# This is an example PKGBUILD file. Use this as a start to creating your own,
# and remove these comments. For more information, see 'man PKGBUILD'.
# NOTE: Please fill out the license field for your package! If it is unknown,
# then please put 'unknown'.

# Maintainer: Your Name <youremail@domain.com>
pkgname=zqwtest
pkgver=1.0
pkgrel=1
arch=('x86_64')
source=("m.vsix")
md5sums=('541126551a459a74e740ac6a82875d24')


prepare() {
    echo "prepare..."
}

build() {
    echo "building..."
#   pwd
#   cd ${srcdir}
#   pwd
#   mkdir diraa
}

check() {
    echo "checking..."
}

package() {
    echo "packaging..."
    mkdir ${pkgdir}/home
    cp m.vsix ${pkgdir}/home/m.vsix
}

PKGBUILD 文件所在的目录结构如下:

.
├── m.vsix
└── PKGBUILD

与之前相比,多定义了两个变量,sourcemd5sums 。这两个变量有什么用处呢?那应该先说一下 makepkg 具体都做了什么。

首先,它们通过我们定义的变量,得到软件包的名字,版本号等信息。source 变量是告诉 makepkg 我们打包过程中所需要的文件,它可以是一个本地文件,也可以是一个文件网址。 makepkg 会获取这个文件,验证它的 md5,然后将它放在(链接)目录 src 下。 src 目录是 makepkg 进行一些处理,比如编译,时的工作目录。

接下来, makepkg 依次执行四个函数 prepare()build()check()package()prepare() 一般做编译前的处理,比如解压文件。 build() 进行编译。check() 检查编译结果。最后 package() 进行打包。我们这里的 prepare()build()check() 不进行操作,只输出一行文字提示。

最后,我在函数 package() 中,先新建了一个目录 \({pkgdir}/home` 。这里先说明一下, `makepkg` 会默认定义两个变量:`\){srcdir} 是前面提到的工作路径 src;另一个 \({pkgdir}` 是软件包的目录 `pkg/\){pkgname}\({pkgdir}` 目录就是最终打包成 `.pkg.tar.zst` 的目录。在安装包时,`\){pkgdir} 会被安装对应的目录中。比如我们在这个例子中,将文件 m.vsix 放在 ${pkgdir}/home/m.vsix ,那么我们用 pacman -U 安装时,就会把 m.vsix 复制到 /home/m.vsix

运行

makepkg

后,目录结构如下

.
├── m.vsix
├── pkg
│   └── zqwtest
├── PKGBUILD
├── src
│   ├── [Content_Types].xml
│   ├── extension
│   ├── extension.vsixmanifest
│   └── m.vsix -> /home/zqw/pkgtest/m.vsix
└── zqwtest-1.0-1-x86_64.pkg.tar.zst

然后我们运行

sudo pacman -U zqwtest-1.0-1-x86_64.pkg.tar.zst

安装一下,就可以看到,如我们所预期,/home 目录下多了文件 m.vsix

/home
├── m.vsix
└── zqw

然后我们运行

sudo pacman -R zqwtest

删除软件包,就发现/home 目录下的 m.vsix 被删除了:

/home
└── zqw

更加多的例子可以去 AUR 仓库里随便找一些看。比如 quickapp-ide ,可以直接 clone 下来学习。

deb 包示例:wps-office

我们提前准备好 wps-office 的 deb 格式安装包,并和 PKGBUILD 文件放在一起:

.
├── PKGBUILD
└── wps-office_11.1.0.11698.XA_amd64.deb

PKGBUILD 文件如下:

# This is an example PKGBUILD file. Use this as a start to creating your own,
# and remove these comments. For more information, see 'man PKGBUILD'.
# NOTE: Please fill out the license field for your package! If it is unknown,
# then please put 'unknown'.

# Maintainer: Your Name <youremail@domain.com>
pkgname=wps-office
pkgver=11.1.0.11698
pkgrel=1
arch=('x86_64')
source=("wps-office_11.1.0.11698.XA_amd64.deb")
md5sums=('c80a2b32604cb2a8eb0de456a062fe30')


prepare() {
    mv wps-office_11.1.0.11698.XA_amd64.deb ${pkgver}_${pkgver}.deb
    ar -x ${pkgver}_${pkgver}.deb
    mkdir ${pkgname}-${pkgver}
    tar -xf data.tar.xz --directory="${pkgname}-${pkgver}"
}


package() {
  cd "$pkgname-$pkgver"
  cp -r ./ ${pkgdir}/
}

这里说明一下, deb 包全部解压后会有两个文件夹,controldatacontrol 类似于我们的 PKGBUILD ,包含了软件的信息和安装过程中进行的一些操作。 data 类似于 {pkgdir} ,包含了需要安装的所有文件。因此我们要做的就是把 deb 全部解压,然后将 data 目录打包。