跳转到内容

Go 语言软件打包准则

来自 Arch Linux 中文维基
Arch 打包准则

32 位安全CLRCMakeDKMSEclipseElectronFree PascalGNOMEGoHaskellJava交叉编译工具KDELispMesonMinGW内核模块Node.jsNonfreeOCamlPerlPHPPythonRRubyRustShellVCSWebWine字体

本文涵盖了为 Go 软件包编写 PKGBUILD 时须遵循的标准和指引。

通用准则

命名

如果软件包提供的程序与 Go 生态存在强关联,请使用 go-模块名称。对于其它应用,请仅使用程序名。

注意:软件包名称只允许使用小写字符。

构建

依赖

Go 1.11 为 go 模块提供了初步支持,使得上游的 Go 代码可以声明依赖并将其固定到特定项目版本。Currently our packaging efforts utilize this to vendor dependencies.

上游项目不使用 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 模块

go 默认会使用 GOPATH 来下载和存放 go 模块,并导致用户的 ~/go 目录大小增加。

为将所有 go 模块保留在构建环境中,可以在准备步骤(prepare)中配置 GOPATH="${srcdir}",然后将 go 模块下载到软件包源码目录(srcdir)中:

PKGBUILD
prepare() {
  cd "${pkgname}-${pkgver}"
  export GOPATH="${srcdir}"
  go mod download -modcacherw
}

构建标志和选项

Go 不会自动将系统的构建标志(例如 CFLAGSLDFLAGS)传递给 C 工具链。为使用 RELRO 和其它加固标志构建 Go 二进制文件,需要在构建环境中显式指定 CGO_CFLAGSCGO_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}\"" \
    .
注意:大多数用于 Go 应用的 Makefile 会覆盖掉 GOFLAGS,不会将上述标志传递给编译器。如果上游项目使用了 Makefile,则需要通过补丁使其遵循传入的标志,或是直接调用 go build 来绕过该文件。

标志功能

  • -buildmode=pie 会启用 PIE 编译,用于加固二进制文件。
  • -trimpath 用于可复现构建,可以防止嵌入完整构建路径和模块路径。
  • -mod=readonly 可以保证模块文件不会在 go 的任何操作中被更改。
  • -modcacherw is not important, but it ensures that go modules creates a write-able path. Default is read-only.
提示:如果软件的 modules.txt 中包含了一个 vendor 目录,那可以将 -mod 标志修改为 -mod=vendor
警告:包维护者需要确保构建标志被正确传递给了编译器,请务必检查源码的 Makefile

支持调试包

Enabling debug packages with source listing and proper symbol look ups require a few modifications to the default buildflags.

  • Removal of -trimpath to ensure source paths are rewritten in the binary
  • Include -compressdwarf=false in -ldflags to ensure we can parse the DWARF headers as current tooling does not support compressed headers.
  • Ensure -linkmode=external as the internal linker go uses does not embed a build-id into the binary.
  • Include GOPATH="${srcdir}" so makepkg can include the source code for all modules.

The above options should produce a debug package with proper detached symbols and source listings which can then be picked up by the debugger.

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
}

软件包示例