跳至內容

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
}

軟體包示例