abuild note

https://mdb.koi.moe/ 本册子正在完善中

Alpine Linux 中文社区

Alpine Linux 官方 Wiki

Alpine Linux 包搜索

APORTS 仓库

其他发行版的包

本站内容使用 CC BY-NC-SA 4.0 许可证发布

构建环境

https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package

alpine-sdk

元包,包括以下三个包(安装 meta 包后,依赖会自动安装)

build-base

也是元包,一部分是 abuild 需要的运行时依赖:

  • binutils 构建相关,链接小工具
  • file 检测文件类型
  • patch 打 patch 用,musl 发行版需要很多 patch,还有上游的更新补丁

另一部分是大部分构建都需要的工具链:

  • gccg++
  • make
  • libc-dev 约等于 musl-dev
  • fortify-headers 缓冲区检查宏

所以 APKBUILD 中 makedepends 不需要再把 gcc make 写进来啦!每台打包机都默认安装了这些包

abuild

bin/abuild

根据 APKBUILD 拉取源码并更新配置文件中的校验值(构建需要)

$ abuild checksum

自动安装依赖并在构建完成后卸载依赖,自动完成整个构建过程

$ abuild -r

启动沙盒环境进行纯净构建,等同于手动创建 chroot 环境(需要安装 abuild-rootbld 包)

$ abuild rootbld

bin/newapkbuild

newapkbuild 使用模板新建(会自动创建文件夹)

$ newapkbuild -h
newapkbuild 3.12.0-r0 - generate a new APKBUILD
Usage: newapkbuild [-n PKGNAME] [-d PKGDESC] [-l LICENSE] [-u URL]
       [-a | -C | -m | -p | -y | -r] [-s] [-c] [-f] [-h]
       PKGNAME[-PKGVER] | SRCURL
Options:
  -n  Set package name to PKGNAME (only use with SRCURL)
  -d  Set package description to PKGDESC
  -l  Set package license to LICENSE, use identifiers from:
      <https://spdx.org/licenses/>
  -u  Set package URL
  -a  Create autotools package (use ./configure ...)
  -C  Create CMake package (Assume cmake/ is there)
  -m  Create meson package (Assume meson.build is there)
  -p  Create perl package (Assume Makefile.PL is there)
  -y  Create python setuptools package (Assume setup.py is there)
  -e  Create python gpep517 package (Assume pyproject.toml is there)
  -r  Create rust package (Assume Cargo.toml is there)
  -s  Use sourceforge source URL
  -c  Copy a sample init.d, conf.d, and install script
  -f  Force even if directory already exists
  -h  Show this help

abuild-rootbld

$ apk add abuild-rootbld

注意事项:https://wiki.alpinelinux.org/wiki/Include:AbuildRootBld

  1. options 添加 net
  2. 上级目录下添加 .rootbld-repositories 文件指定仓库或镜像(官方的 aports 自带)
  3. 保持文件结构像 ~/aports 或设置环境变量 APORTSDIR=$(PWD)

由于每次都是重新下载安装环境,因此可以开启安装包缓存

$ setup-apkcache

以及指定镜像源来减少等待时间。

$ mirror=http://mirrors.tuna.tsinghua.edu.cn/alpine abuild rootbld

这里的环境变量 mirror 生效是因为官方 aports 的 .rootbld-repositories 中有预留变量

lab ~/aports/community
$ bat .rootbld-repositories -p
$mirror/$version/main
$mirror/$version/community

常见问题

网络问题拉不下来构建需要的源码

abuild 内部调用的是 wget,设置 http 代理

$ https_proxy=http://localhost:7890/ abuild checksum

如何知道软件需要的依赖?

makedepends:abuild rootbld 一个一个添加 dev 包测试

depends:打包结束时 abuild 会自动识别大部分动态链接库并形成依赖关系,手动加上额外的运行时依赖,及软件间接调用的 git、bash、less 等

软件 A 依赖本地软件 B(另外一个未上传的本地包)

$ apk add --allow-untrusted xxxx.apk

abuild rootbld 时,添加 ~/packages/testing/~/aports/testing/.rootbld-repositories

编译需要的 gcc 是依赖吗?

build-base

由于 musl 引起的编译错误如何解决?

应该都有人遇到过,aports 搜一搜自己也做个补丁

反复下载安装依赖耗时很久

abuild-rootbld

查看 apk 中的文件列表

$ tar -vtf file.apk

APKBUILD

Maintainer 声明

# Maintainer 需要放在 # Contributor 后。 以便在简单的更改(如只修改 pkgverpkgrel) 时,能够在默认的 diff 3 行信息中看到 Maintainer 信息。

license

该标签反映上游项目的许可证,检查 COPYING、LICENSE 或源码文件的许可证标头。 开源项目的许可证应采用 SPDX License List1 或 SPDX License Exceptions2 中规定的标准标识符

部分许可证要求在所有副本中包含版权声明、许可声明和源代码,因此需要在 package() 阶段同样 install 这些文件。

设置 subpackages="$pkgname-doc" 会自动检出相关许可文件到 -doc 子包,减小主包体积。

source

包含多个部分:

  1. 远程拉取的源码归档包
  2. 本地 patch 补丁
  3. 本地 openrc 配置文件(initd & confd)

但不包括 install-script

cache

远端文件会先调用 curl 或 wget3 缓存到本地 /var/cache/distfiles4

如果缓存冲突或者 rename 操作失误可以随时来此删除

环境变量参考 cURL 文档:Proxy environment variables

与 Arch 不同,如果是以打包压缩后缀 tar.gz 等结尾,后续构建会自动解压到 srcdir

rename

远端文件以文件名为缓存,因此应避免冲突,遵守 $pkgname-$pkgver.xxx 的命名规则。

如有需要,使用重命名语法 <name>::<remote-link>

$pkgname-$pkgver.tar.gz::https://github.com/software/software/archive/v$pkgver.tar.gz

可使用 wget --spider -S http://example.com/file.zip 查看文件名是否满足要求。

GitHub 项目的源码归档文件下载链接参考文档5,常用 tag 和 commit

https://github.com/<user>/<repo>/archive/<commit-hash>.tar.gz
https://github.com/qaqland/display-test/archive/9e3a201a102e3e08447b75cfa0d402fac16bd496.tar.gz

https://github.com/qaqland/display-test/archive/refs/tags/v0.0.2.tar.gz
https://github.com/<user>/<repo>/archive/refs/tags/<tag-name>.tar.gz
4

Alpine Linux 官方的缓存可以在这里查看 https://distfiles.alpinelinux.org/distfiles/

builddir

指向 source 解压之后的文件夹(后续构建的工作目录),一般有:

  • $srcdir/$pkgname-$pkgver(由于是默认值 apkbuild-lint 会提示您删掉)
  • $srcdir/v$pkgver(某些 GitHub 自动生成的 release)

可以首先使用默认值观察是否成功构建,否则前往 $srcdir 观察解压后的目录

shell

参数替换

默认值

GOCACHE=${GOCACHE:-"$srcdir/go-cache"}

如果 GOCACHE 为空或未设置,使用 "$srcdir/go-cache" 作为默认值

替换

pkgver=${_pkgver/-/.}

第一个匹配到的 - 将会被替换为 .,用于从 2.0.0-10 转换为 2.0.0.10

checkdepends="${checkdepends/binutils-gold/}"

在变量 checkdepends 的值中,将第一个匹配到的 binutils-gold 替换为空字符串(因为在第二个斜杠后面没有提供替换字符串)。即将删除 checkdepends 的值中的第一个 binutils-gold

删前缀

json=${subpkgname#libmustach-}

删去字符串前缀,如果 $subpkgname 的值是 libmustach-json-c,那么 $json 的值就是 json-c

删后缀

json=${jsondev%-dev}

删去字符串后缀,如果 $jsondev 的值是 json-c-dev,那么 $json 的值就是 json-c

Go

GOARCH 与 CARCH 不一致

一般不用设置,部分上游构建脚本(为交叉编译)需要手动指定编译架构

参考 aports/community/go/APKBUILD1

其中 CTARGET_ARCH 在交叉编译时用,一般不用考虑,直接写 CARCH 就好。

 # Contributor: Sören Tempel <soeren+alpine@soeren-tempel.net>
 # Contributor: Eivind Uggedal <eu@eju.no>
 # Contributor: Natanael Copa <ncopa@alpinelinux.org>
 # Maintainer: Sören Tempel <soeren+alpine@soeren-tempel.net>
 pkgname=go
 # go binaries are statically linked, security updates require rebuilds
 pkgver=1.22.2
 pkgrel=0
 pkgdesc="Go programming language compiler"
 url="https://go.dev/"
 arch="all"
 license="BSD-3-Clause"
 depends="binutils gcc musl-dev"
 makedepends="bash"
 checkdepends="binutils-gold git git-daemon"
 subpackages="$pkgname-doc"
 source="https://go.dev/dl/go$pkgver.src.tar.gz
 	0001-cmd-link-prefer-musl-s-over-glibc-s-ld.so-during-dyn.patch
 	0002-misc-cgo-test-enable-setgid-tests-on-Alpine-Linux-ag.patch
 	0003-go.env-Don-t-switch-Go-toolchain-version-as-directed.patch
 	0004-cmd-dist-cmd-go-define-assembly-macros-handle-GOARM-.patch
 
 	tests-fchmodat-not-supported.patch
 	"
 case "$CARCH" in
 	arm*|aarch64) depends="binutils-gold";;
 	riscv64|loongarch64)
 		# binutils-gold is not supported on riscv64 and loongarch64.
 		checkdepends="${checkdepends/binutils-gold/}"
 		;;
 esac
 
 # secfixes:
 #   0:
 #     - CVE-2022-41716
 #     - CVE-2022-41720
 #     - CVE-2022-41722
 #   1.22.2-r0:
 #     - CVE-2023-45288
 #   1.22.1-r0:
 #     - CVE-2024-24783
 #     - CVE-2023-45290
 #     - CVE-2023-45289
 #     - CVE-2024-24785
 #     - CVE-2024-24784
 #   1.21.5-r0:
 #     - CVE-2023-39324
 #     - CVE-2023-39326
 #   1.21.3-r0:
 #     - CVE-2023-39325
 #     - CVE-2023-44487
 #   1.21.2-r0:
 #     - CVE-2023-39323
 #   1.21.1-r0:
 #     - CVE-2023-39318
 #     - CVE-2023-39319
 #     - CVE-2023-39320
 #     - CVE-2023-39321
 #     - CVE-2023-39322
 #   1.20.7-r0:
 #     - CVE-2023-29409
 #   1.20.6-r0:
 #     - CVE-2023-29406
 #   1.20.5-r0:
 #     - CVE-2023-29402
 #     - CVE-2023-29403
 #     - CVE-2023-29404
 #     - CVE-2023-29405
 #   1.20.4-r0:
 #     - CVE-2023-24539
 #     - CVE-2023-24540
 #     - CVE-2023-29400
 #   1.20.3-r0:
 #     - CVE-2023-24537
 #     - CVE-2023-24538
 #     - CVE-2023-24534
 #     - CVE-2023-24536
 #   1.20.2-r0:
 #     - CVE-2023-24532
 #   1.20.1-r0:
 #     - CVE-2022-41725
 #     - CVE-2022-41724
 #     - CVE-2022-41723
 #   1.19.4-r0:
 #     - CVE-2022-41717
 #   1.19.2-r0:
 #     - CVE-2022-2879
 #     - CVE-2022-2880
 #     - CVE-2022-41715
 #   1.19.1-r0:
 #     - CVE-2022-27664
 #     - CVE-2022-32190
 #   1.18.5-r0:
 #     - CVE-2022-32189
 #   1.18.4-r0:
 #     - CVE-2022-1705
 #     - CVE-2022-1962
 #     - CVE-2022-28131
 #     - CVE-2022-30630
 #     - CVE-2022-30631
 #     - CVE-2022-30632
 #     - CVE-2022-30633
 #     - CVE-2022-30635
 #     - CVE-2022-32148
 #   1.18.1-r0:
 #     - CVE-2022-28327
 #     - CVE-2022-27536
 #     - CVE-2022-24675
 #   1.17.8-r0:
 #     - CVE-2022-24921
 #   1.17.7-r0:
 #     - CVE-2022-23772
 #     - CVE-2022-23773
 #     - CVE-2022-23806
 #   1.17.6-r0:
 #     - CVE-2021-44716
 #     - CVE-2021-44717
 #   1.17.3-r0:
 #     - CVE-2021-41772
 #     - CVE-2021-41771
 #   1.17.2-r0:
 #     - CVE-2021-38297
 #   1.17.1-r0:
 #     - CVE-2021-39293
 #   1.17-r0:
 #     - CVE-2020-29509
 #     - CVE-2020-29511
 #     - CVE-2021-29923
 #   1.16.7-r0:
 #     - CVE-2021-36221
 #   1.16.6-r0:
 #     - CVE-2021-34558
 #   1.16.5-r0:
 #     - CVE-2021-33195
 #     - CVE-2021-33196
 #     - CVE-2021-33197
 #     - CVE-2021-33198
 #   1.16.4-r0:
 #     - CVE-2021-31525
 #   1.16.2-r0:
 #     - CVE-2021-27918
 #     - CVE-2021-27919
 #   1.15.7-r0:
 #     - CVE-2021-3114
 #     - CVE-2021-3115
 #   1.15.5-r0:
 #     - CVE-2020-28362
 #     - CVE-2020-28366
 #     - CVE-2020-28367
 #   1.15.2-r0:
 #     - CVE-2020-24553
 #   1.15-r0:
 #     - CVE-2020-16845
 #   1.14.5-r0:
 #     - CVE-2020-15586
 #   1.13.7-r0:
 #     - CVE-2020-7919
 #   1.13.2-r0:
 #     - CVE-2019-17596
 #   1.13.1-r0:
 #     - CVE-2019-16276
 #   1.12.8-r0:
 #     - CVE-2019-9512
 #     - CVE-2019-9514
 #     - CVE-2019-14809
 #   1.11.5-r0:
 #     - CVE-2019-6486
 #   1.9.4-r0:
 #     - CVE-2018-6574
 
 if [ "$CBUILD" = "$CTARGET" ]; then
 	makedepends="go-bootstrap $makedepends"
 	provides="go-bootstrap=$pkgver-r$pkgrel"
 else
 	pkgname="go-bootstrap"
 	makedepends="go $makedepends"
 	# Go expect host linker instead of the cross-compiler
 	export CC_FOR_TARGET="$CC"
 	export CC="${HOSTLD:-gcc}"
 	export CXX="${HOSTLD:-g++}"
 	export LD="${HOSTLD:-ld}"
 fi
 
case "$CTARGET_ARCH" in
aarch64) export GOARCH="arm64" ;;
armel)   export GOARCH="arm" GOARM=5 ;;
armhf)   export GOARCH="arm" GOARM=6 ;;
armv7)   export GOARCH="arm" GOARM=7 ;;
s390x)   export GOARCH="s390x" ;;
x86)     export GOARCH="386" ;;
x86_64)  export GOARCH="amd64" ;;
ppc64)   export GOARCH="ppc64" ;;
ppc64le) export GOARCH="ppc64le" ;;
riscv64) export GOARCH="riscv64" ;;
loongarch64) export GOARCH="loong64" ;;
*)       export GOARCH="unsupported";;
esac

 compile go itself as a PIE on supported arches.
case "$CARCH" in
x86_64|s390x|aarch64) export GO_LDFLAGS=-buildmode=pie ;;
esac
 
 prepare() {
 	default_prepare
 
 	# The GitLab CI builds aports in a container. On ppc64le, ASLR
 	# needs to be disabled in order to have the following test case
 	# pass. However, the container doesn't have permissions to
 	# disable ASLR, hence we just disable this test for now.
 	#
 	# See https://github.com/golang/go/issues/49066#issuecomment-1252948861
 	if [ "$CTARGET_ARCH" = "ppc64le" ]; then
 		rm test/fixedbugs/bug513.go
 	fi
 }
 
 builddir="$srcdir"/go
 build() {
 	cd "$builddir/src"
 
 	export GOOS="linux"
 	export GOPATH="$srcdir"
 	export GOROOT="$builddir"
 	export GOBIN="$GOROOT"/bin
 	export GOROOT_FINAL=/usr/lib/go
 
 	local p; for p in /usr/lib/go-bootstrap /usr/lib/go-linux-$GOARCH-bootstrap /usr/lib/go; do
 		if [ -d "$p" ]; then
 			export GOROOT_BOOTSTRAP="$p"
 			break
 		fi
 	done
 
 	./make.bash -v
 
 	# copied from bootstrap.bash to fixup cross-built bootstrap go
 	if [ "$CBUILD" != "$CTARGET" ]; then
 		local gohostos="$(../bin/go env GOHOSTOS)"
 		local gohostarch="$(../bin/go env GOHOSTARCH)"
 		mv ../bin/*_*/* ../bin
 		rmdir ../bin/*_*
 		rm -rf "../pkg/${gohostos}_$gohostarch"* "../pkg/tool/${gohostos}_$gohostarch"*
 		rm -rf ../pkg/bootstrap ../pkg/obj
 	fi
 }
 
 check() {
 	cd "$builddir/src"
 	if [ "$CTARGET_ARCH" = "armhf" ]; then
 		export GO_TEST_TIMEOUT_SCALE=2
 	fi
 
 	# Test suite does not pass with ccache, thus remove it form $PATH.
 	export PATH="$(echo "$PATH" | sed 's|/usr/lib/ccache/bin:||g')"
 
 	PATH="$builddir/bin:$PATH" ./run.bash -no-rebuild
 }
 
 package() {
 	mkdir -p "$pkgdir"/usr/bin "$pkgdir"/usr/lib/go/bin "$pkgdir"/usr/share/doc/go
 
 	for binary in go gofmt; do
 		install -Dm755 bin/"$binary" "$pkgdir"/usr/lib/go/bin/"$binary"
 		ln -s /usr/lib/go/bin/"$binary" "$pkgdir"/usr/bin/
 	done
 
 	cp -a misc pkg src lib "$pkgdir"/usr/lib/go
 	cp -r doc "$pkgdir"/usr/share/doc/go
 	rm -rf "$pkgdir"/usr/lib/go/pkg/obj
 	rm -rf "$pkgdir"/usr/lib/go/pkg/bootstrap
 	rm -f  "$pkgdir"/usr/lib/go/pkg/tool/*/api
 
 	# Install go.env, see https://go.dev/doc/toolchain#GOTOOLCHAIN.
 	install -Dm644 "$builddir"/go.env "$pkgdir"/usr/lib/go/go.env
 	install -Dm644 VERSION "$pkgdir/usr/lib/go/VERSION"
 
 	# Remove tests from /usr/lib/go/src to reduce package size,
 	# these should not be needed at run-time by any program.
 	find "$pkgdir"/usr/lib/go/src \( -type f -a -name "*_test.go" \) \
 		-exec rm -rf \{\} \+
 	find "$pkgdir"/usr/lib/go/src \( -type d -a -name "testdata" \) \
 		-exec rm -rf \{\} \+
 
 	# Remove rc (plan 9) and bat scripts (windows) to reduce package
 	# size further. The bash scripts are actually needed at run-time.
 	#
 	# See: https://gitlab.alpinelinux.org/alpine/aports/issues/11091
 	find "$pkgdir"/usr/lib/go/src -type f -a \( -name "*.rc" -o -name "*.bat" \) \
 		-exec rm -rf \{\} \+
 }
 
 sha512sums="
 f2491d2b5d4ef2dd86ca7820503a2534cd1860822049dc01a6cb40b556a0812cfc4196fa83173765816060253ac949f4165b0fb4b2bed5d45e30d03bb69e434d  go1.22.2.src.tar.gz
 34dbe032c5f08dd8a7aad36fc4d54e746a876fdadc25466888a2f04f5a9d53103190ebd68d3cf978d3a041976185e30ffb25611fb577d031c159810d2d4c7c41  0001-cmd-link-prefer-musl-s-over-glibc-s-ld.so-during-dyn.patch
 89ab4fbb2901d3907e9661dce877ee45b4a4ee07b964dca341235420ee08764f49aed5da1596d28c649e349af19ea49c03ab6f2c2ad7588a4cf950a619c10e9b  0002-misc-cgo-test-enable-setgid-tests-on-Alpine-Linux-ag.patch
 8061e4ef9d7dd31804bd8d98c95afa5dd82567940b3436f45f874e0419e324b49713d8a814df04617e575ec3c6155199c4661352ea8aef63ead81ca3020f3dc4  0003-go.env-Don-t-switch-Go-toolchain-version-as-directed.patch
 a69a836364be8857f153b606769a155d89fdbbac39af6fbbc3cd923e95a15805f7497d6fdce6176a18a9ccee867946a03b809d8a4a32765dd20086115f179929  0004-cmd-dist-cmd-go-define-assembly-macros-handle-GOARM-.patch
 33ecefca77fa0af52a3b2b66a76977af27a88c8dddb89f03e0a5ae6794b9aac53a62d7be33020b49022e9a89d4cdfa383038ee10e160eb94548b2430bf3cfb5e  tests-fchmodat-not-supported.patch
 "

避免 Go 缓存污染外部环境

WIP2

Python

venv

In check() part, setup venv and use python from there:

python3 -m venv --clear --system-site-packages .testenv
.testenv/bin/python3 -m installer xxx.whl
.testenv/bin/python3 -m pytest

PATH

Export $PATH if needed (some test call binrary)

export PATH="$builddir"/.testenv/bin:$PATH

Be careful that all runpart check() and package() are in the same process sharing same environment.

pip

By default, pip install source files within "another" isolation, but we usually want to use packages from system and build():

.testenv/bin/python3 -m pip install --no-build-isolation --no-index xxx