Go 语言软件打包准则
32 位 – 安全 – CLR – CMake – DKMS – Eclipse – Electron – Free Pascal – GNOME – Go – Haskell – Java – 交叉编译工具 – KDE – Lisp – Meson – MinGW – 内核模块 – Node.js – Nonfree – OCaml – Perl – PHP – Python – R – Ruby – Rust – Shell – VCS – Web – Wine – 字体
本文涵盖了为 Go 软件包编写 PKGBUILD 时须遵循的标准和指引。
如果软件包提供的程序与 Go 生态存在强关联,请使用 go-模块名称。对于其它应用,请仅使用程序名。
Go 1.11 为 go 模块提供了初步支持,使得上游的 Go 代码可以声明依赖并将其固定到特定项目版本。目前,我们的打包工作利用了这一点来满足 go 模块依赖关系。
对于不使用 Go 模块的上游代码,可以使用以下变通方法,并请考虑向上游提出 issue:
PKGBUILD
url=https://github.com/upstream_user/upstream_project
prepare() {
cd "$pkgname-$pkgver"
go mod init "${url#https://}" # strip https:// from canonical URL
go mod tidy
}
go 默认会使用 GOPATH 来下载和存放 go 模块,并导致用户的 ~/go 目录大小增加。
为将所有 go 模块保留在构建环境中,可以在准备步骤(prepare)中配置 GOPATH="${srcdir}",然后将 go 模块下载到软件包源码目录(srcdir)中:
PKGBUILD
prepare() {
cd "${pkgname}-${pkgver}"
export GOPATH="${srcdir}"
go mod download -modcacherw
}
Go 不会自动将系统的构建标志(例如 CFLAGS 和 LDFLAGS)传递给 C 工具链。为使用 RELRO 和其它加固标志构建 Go 二进制文件,需要在构建环境中显式指定 CGO_CFLAGS 和 CGO_LDFLAGS 等相关变量:
export CGO_CPPFLAGS="${CPPFLAGS}"
export CGO_CFLAGS="${CFLAGS}"
export CGO_CXXFLAGS="${CXXFLAGS}"
export CGO_LDFLAGS="${LDFLAGS}"
export GOFLAGS="-buildmode=pie -trimpath -ldflags=-linkmode=external -mod=readonly -modcacherw"
# 或者也可以通过命令行选项指定部分标志
go build \
-trimpath \
-buildmode=pie \
-mod=readonly \
-modcacherw \
-ldflags "-linkmode external -extldflags \"${LDFLAGS}\"" \
.
GOFLAGS,不会将上述标志传递给编译器。如果上游项目使用了 Makefile,则需要通过补丁使其遵循传入的标志,或是直接调用 go build 来绕过该文件。-buildmode=pie会启用 PIE 编译,用于加固二进制文件。-trimpath用于可复现构建,可以防止嵌入完整构建路径和模块路径。-mod=readonly可以保证模块文件不会在 go 的任何操作中被更改。-modcacherw不重要,但是可以保证 go 模块会创建一个可写的路径。默认只会创建一个只读的路径。
modules.txt 中包含了一个 vendor 目录,那可以将 -mod 标志修改为 -mod=vendor。Makefile。要启用带有源代码和符号查找的调试包,需要对默认构建标志进行一些修改。
- 删除
-trimpath来确保源码路径被写入二进制文件。 - 由于目前的工具不支持压缩的 DWARF 头,因此需要在
-ldflags中包含-compressdwarf=false来确保工具能解析 DWARF 头。 - 使用
-linkmode=external,因为 go 内部的链接器不会把 build-id 嵌入二进制文件。 - 包含
GOPATH="${srcdir}"以使 makepkg 能包含所有模块的源码。
使用上述选项应该能生成包含正确的分离符号和源代码,并能被调试器正确读取的调试包。
export CGO_CPPFLAGS="${CPPFLAGS}"
export CGO_CFLAGS="${CFLAGS}"
export CGO_CXXFLAGS="${CXXFLAGS}"
export CGO_LDFLAGS="${LDFLAGS}"
export GOPATH="${srcdir}"
export GOFLAGS="-buildmode=pie -mod=readonly -modcacherw"
go build -ldflags "-compressdwarf=false -linkmode external" .
有多种方法可以构建项目中的所有 go 二进制文件。
build(){
cd "$pkgname-$pkgver"
go build -o output-binary .
}
... 是一个简写,它会让编译器递归遍历所有目录,并找到所有二进制文件。可以将其搭配输出目录使用以构建所有文件:
prepare(){
cd "$pkgname-$pkgver"
mkdir -p build
}
build(){
cd "$pkgname-$pkgver"
go build -o build ./cmd/...
}
pkgname=foo
pkgver=0.0.1
pkgrel=1
pkgdesc='Go PKGBUILD Example'
arch=('x86_64')
url="https://example.org/$pkgname"
license=('GPL')
makedepends=('go')
source=("$url/$pkgname-$pkgver.tar.gz")
sha256sums=('1337deadbeef')
prepare(){
cd "$pkgname-$pkgver"
mkdir -p build/
}
build() {
cd "$pkgname-$pkgver"
export CGO_CPPFLAGS="${CPPFLAGS}"
export CGO_CFLAGS="${CFLAGS}"
export CGO_CXXFLAGS="${CXXFLAGS}"
export CGO_LDFLAGS="${LDFLAGS}"
export GOFLAGS="-buildmode=pie -trimpath -ldflags=-linkmode=external -mod=readonly -modcacherw"
go build -o build ./cmd/...
}
check() {
cd "$pkgname-$pkgver"
go test ./...
}
package() {
cd "$pkgname-$pkgver"
install -Dm755 build/$pkgname "$pkgdir"/usr/bin/$pkgname
}