Go package guidelines - ArchWiki

# Go package guidelines

From ArchWiki
Jump to navigation Jump to search

This document covers standards and guidelines on writing PKGBUILDs for Go.

## General guidelines

### Package naming

For Go library modules, use `go-modulename`. Also use the prefix if the package provides a program that is strongly coupled to the Go ecosystem. For other applications, use only the program name.

Note: The package name should be entirely lowercase.

### Instructions for alternative dependency managers

This is not needed for Go 1.11 and later, unless an alternative dependency manager is required by the Go project you are packaging.

When preparing the sources before building, the following may be needed:

- Create a directory `$srcdir/gopath` for $GOPATH and copy over the source to this directory.
- It should be noted that this step might not be needed if the project provides a `Makefile` for the project that sets this up.

    prepare(){
      mkdir -p gopath/src/github.com/pkgbuild-example
      ln -rTsf $pkgname-$pkgver gopath/src/github.com/pkgbuild-example/$pkgname

      # the dependencies can be fetched here if needed
      cd gopath/src/github.com/pkgbuild-example/$pkgname
      dep ensure
    }

## Building

There are two go packages in the repositories that you can build towards; go and go-pie. All packages should preferably be built towards go-pie as this enables us to deliver secure binaries. However, as upstream might have bugs, building towards go should be a last resort.

### Flags and build options

Most Makefiles written for go applications do not respect the `LDFLAGS` provided by build systems, this causes Go binaries to not be compiled with RELRO. This needs to be patched into the Makefile, or the Makefile should be omitted. If there is an Makefile involved, you have 3 options to support RELRO.

- Patch Makefile
- Skip Makefile completely and use `go build`
- Export `GOFLAGS` - This is less desirable as we are dropping flags from `LDFLAGS`.

For reproducible builds it's important that the binaries are stripped of the build path using the `-trimpath` flags.

    # LDFLAGS into the GOFLAGS env variable.
    export GOFLAGS="-gcflags=all=-trimpath=${PWD} -asmflags=all=-trimpath=${PWD} -ldflags=-extldflags=-zrelro -ldflags=-extldflags=-znow"

    # or alternatively use LDFLAGS defined in go build.
    go build \
        -gcflags "all=-trimpath=${PWD}" \
        -asmflags "all=-trimpath=${PWD}" \
        -ldflags "-extldflags ${LDFLAGS}" \
        .

Note: For the sake of brevity, these flags are omitted in the examples below.

### Modern Go projects (for Go >=1.11)

Go 1.11 introduces go modules. This omits the need to setup `GOPATH` and we can build directly in the directory.

Projects like these have a `go.mod` and a `go.sum` file.

Note: These builds needs `$PWD` stripped from the binary.

    build(){
      cd "$pkgname-$pkgver"
      go build .
    }

### Old Go projects (for Go <1.11)

When building go packages with $GOPATH there are a few problems one can encounter. Usually the project delivers a `Makefile` that can be used and should be used. There are cases where you still need to setup a `$GOPATH` in the PKGBUILD. The following snippet sets up an appropriate `GOPATH` inside `$srcdir/gopath` one can use to build packages. A new directory is used as some go dependency managers do weird things if they discover the project in the root of the `$GOPATH`.

Note: These builds should have `$GOPATH` stripped from the binary.

    prepare(){
      mkdir -p gopath/src/github.com/pkgbuild-example
      ln -rTsf $pkgname-$pkgver gopath/src/github.com/pkgbuild-example/$pkgname
      export GOPATH="$srcdir"/gopath

      # the dependencies can be fetched here if needed
      cd gopath/src/github.com/pkgbuild-example/$pkgname
      dep ensure
    }

    build(){
      export GOPATH="$srcdir"/gopath
      cd gopath/src/github.com/pkgbuild-example/$pkgname
      go install -v .
    }

Note that `install` and `build` and can do recursive builds if you have a `cmd/` subdirectory with multiple binaries: `go install -v github.com/pkgbuild-example/cmd/...`

### Notes about dependencies and dependency managers

Go packages currently use the `vendor/` folder to handle dependencies in projects. These are usually managed by one of several dependency manager projects. If one is used for the package, this step should be performed in the prepare() function of the PKGBUILD.

Dependencies are normally managed by the `go mod` command, that comes with Go 1.11 or later.

There are also alternative dependency managers:

- dep
- godep
- glide

## Sample PKGBUILDs

### Basic 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-pie')
    source=("$url/$pkgname-$pkgver.tar.gz")
    sha256sums=('1337deadbeef')

    build() {
      cd $pkgname-$pkgver
      go build \
        -gcflags "all=-trimpath=$PWD" \
        -asmflags "all=-trimpath=$PWD" \
        -ldflags "-extldflags $LDFLAGS" \
        -o $pkgname .
    }

    package() {
      cd $pkgname-$pkgver
      install -Dm755 $pkgname "$pkgdir"/usr/bin/$pkgname
    }

### PKGBUILD with GOPATH and dep

    pkgname=foo
    pkgver=0.0.1
    pkgrel=1
    pkgdesc='Go PKGBUILD Example'
    arch=('x86_64')
    url='https://example.org/$pkgname'
    license=('GPL')
    makedepends=('go-pie' 'dep')
    source=("$url/$pkgname-$pkgver.tar.gz")
    sha256sums=('1337deadbeef')

    prepare(){
      mkdir -p gopath/src/example.org/foo
      ln -rTsf $pkgname-$pkgver gopath/src/example.org/foo
      export GOPATH="$srcdir"/gopath
      cd gopath/src/example.org/foo
      dep ensure
    }

    build() {
      export GOPATH="$srcdir"/gopath
      cd gopath/src/example.org/foo
      go install \
        -gcflags "all=-trimpath=$GOPATH" \
        -asmflags "all=-trimpath=$GOPATH" \
        -ldflags "-extldflags $LDFLAGS" \
        -v ./...
    }

    check() {
      export GOPATH="$srcdir"/gopath
      cd gopath/src/example.org/foo
      go test ./...
    }

    package() {
      install -Dm755 gopath/bin/$pkgname "$pkgdir"/usr/bin/$pkgname
    }

## Example packages

- dep
- gopass
- delve
- gitea
- git-lfs

Category:

- Arch package guidelines