Go 包指南
本文档涵盖了为 Go 编写 PKGBUILD 的标准和指南。
通用指南
包命名
如果软件包提供了一个与 Go 生态系统紧密耦合的程序,请使用 go-modulename。对于其他应用程序,请仅使用程序名称。
编译
依赖
Go 1.11 引入了对 Go 模块的初步支持。这允许 Go 上游代码声明依赖项并将其固定到给定的项目版本。目前,我们的打包工作利用这一点来供应商(vendor)依赖项。
不使用 Go 模块的上游项目
对于不使用 Go 模块的上游代码,存在以下变通方法。可以考虑向向上游提交问题。
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 模块的上游项目
默认情况下,Go 将使用 GOPATH 下载和存储 Go 模块。这会扩展用户的 ~/go 目录。
要将所有 Go 模块保留在软件包构建环境中,您可以添加一个准备步骤来配置 GOPATH="${srcdir}",并在软件包的源目录中下载 Go 模块。
PKGBUILD
prepare() {
cd "${pkgname}-${pkgver}"
export GOPATH="${srcdir}"
go mod download -modcacherw
}
标志和构建选项
大多数为 Go 应用程序编写的 Makefiles 不会尊重构建系统提供的构建标志,并且会覆盖 GOFLAGS。这会导致 Go 二进制文件未以 RELRO 编译,因为我们需要为编译器设置 CGO_CFLAGS 和 CGO_LDFLAGS。这需要修补到 Makefile 中,或者应该省略 Makefile。
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"
# or alternatively you can define some of these flags from the CLI options
go build \
-trimpath \
-buildmode=pie \
-mod=readonly \
-modcacherw \
-ldflags "-linkmode external -extldflags \"${LDFLAGS}\"" \
.
标志含义
-buildmode=pie启用 PIE 编译以增强二进制文件安全性。-trimpath对于 可复现构建非常重要,因此不会嵌入完整的构建路径和模块路径。-mod=readonly确保模块文件在任何 Go 操作中都不会被更新。-modcacherw不重要,但它确保 Go 模块创建一个可写路径。默认是只读的。
modules.txt 的 vendor 目录时,可以将 -mod 标志安全地更改为 -mod=vendor。支持调试包
启用带有源代码列表和正确符号查找的调试包需要对默认构建标志进行一些修改。
- 删除
-trimpath以确保源代码路径被重写到二进制文件中。 - 在
-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/...
}
示例 PKGBUILD
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
}