Go package guidelines: Difference between revisions

From ArchWiki
(does not belong on a packaging guidelines page, mentioned in Creating_packages#PKGBUILD_generators)
(Removed lang)
 
(66 intermediate revisions by 16 users not shown)
Line 1: Line 1:
[[Category:Package development]]
[[Category:Arch package guidelines]]
[[ja:Go パッケージガイドライン]]
[[ja:Go パッケージガイドライン]]
[[pt:Go package guidelines]]
[[zh-hans:Go package guidelines]]
[[zh-hans:Go package guidelines]]
{{Package guidelines}}
{{Package guidelines}}


[[Wikipedia:Go (programming language)|Go]] is well supported on Arch Linux.
This document covers standards and guidelines on writing [[PKGBUILD]]s for [[Go]].


The {{Pkg|go}} package contains the '''go''' tool (for running {{Ic|go fix}}, {{Ic|go build}} etc). There is also {{Pkg|gcc-go}} which provides {{Ic|gccgo}}.
== General guidelines ==


== General guidelines ==
=== Package naming ===


=== Naming ===
Use {{ic|go-''modulename''}} if the package provides a program that is strongly coupled to the Go ecosystem. For other applications, use only the program name.
* For applications written in Go, use the name of the application as the package name, in lowercase.
** Be creative if the name is already taken.
* For PKGBUILDS that uses the "go" tool to download the package, only add "-git" to the package name if it is not built from a tarball or a from a tagged release (but from trunk/HEAD).
** Similarly for mercurial packages, only add "-hg" to the package name if it is not a release-revision.
** Extend this pattern for other version control systems.
** The go tool has its own logic for which branch or tag it should use. See {{Ic|go get --help}}.
* Consider adding the name of the author to the package name if there are several applications that are named the same, like {{AUR|dcpu16-kballard}}.
** In general, the most popular packages should be allowed to use the shortest or "best" name.
* Postfixes to the package names (like {{Ic|-hg}}, {{Ic|-git}} or {{Ic|-svn}}) are optional if there are no official releases from the project in question. On one hand, it is common to use them when the package downloads from a VCS. On the other hand, most Go projects do not have any release-tarballs, only the repo which is used for branching/tagging the official release, if it is not ''trunk''. Also, {{Ic|go get}}, which is the "official" way to install Go modules, uses the repositories directly. Use your better judgement.


=== Packaging ===
{{Note|The package name should be entirely lowercase.}}
* Go projects are either just library files, just executables or both. Choose the appropriate way of packaging them. There are several examples below.
* Some Go applications or libraries have not been updated to the latest version of Go yet.
** Running {{Ic|go build -fix}} may often work, but it may have to be fixed by the developer. Report an issue upstream if this is the case.
* Several Go projects do not have a version number or a license file.
** Use license=('unknown') and report an issue to the developer if a license file is missing.
** Use version "0.1", "1" or the git-revision (or equivalent for other version control systems) if the version number is missing.
** Alternatively, use the current date as the version number, in this form {{Ic|YYYYMMDD}}.


== Sample PKGBUILDs ==
== Building ==


=== Sample PKGBUILD for an application written in Go ===
=== Dependencies ===


{{bc|<nowiki># Maintainer: NAME <EMAIL>
Go 1.11 introduced the initial support for [https://github.com/golang/go/wiki/Modules go modules]. This allows Go upstream code to declare dependencies and pin them to the given project version. Currently our packaging efforts utilize this to vendor dependencies.


pkgname=PACKAGE NAME
==== Upstream project without go modules ====
pkgver=1.2.3
pkgrel=1
pkgdesc="PACKAGE DESCRIPTION"
arch=('x86_64' 'i686')
url="http://SERVER/$pkgname/"
license=('MIT')
makedepends=('go')
options=('!strip' '!emptydirs')
source=("http://SERVER/$pkgname/$pkgname-$pkgver.tar.gz")
sha256sums=('00112233445566778899aabbccddeeff')


build() {
For upstream code that does not utilise Go modules, the following workaround exists. Consider filing an issue upstream.
  cd "$pkgname-$pkgver"


  go build
{{hc|PKGBUILD|2=
}
<nowiki>
url=https://github.com/upstream_user/upstream_project


package() {
prepare() {
   cd "$pkgname-$pkgver"
   cd "$pkgname-$pkgver"
  go mod init "${url#https://}" # strip https:// from canonical URL
  go mod tidy
}
</nowiki>
}}


  install -Dm755 "$pkgname-$pkgver" "$pkgdir/usr/bin/$pkgname"
{{Note|Any such hacks makes the package unreproducible as modules will change between package builds.}}
  install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}


# vim:set ts=2 sw=2 et:</nowiki>}}
=== Flags and build options ===


==== Sample packages ====
Most Makefiles written for go applications do not respect the build flags provided by build systems along with overwriting {{ic|GOFLAGS}}, this causes Go binaries to not be compiled with [https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro RELRO] as we need {{ic|CGO_CFLAGS}} and {{ic|CGO_LDFLAGS}} to be set for the compiler. This needs to be patched into the Makefile, or the Makefile should be omitted.
* {{Pkg|gendesk}}
* {{AUR|dcpu16}}


=== Sample PKGBUILD for when only a single source file is available ===
{{bc|1=
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"


{{bc|<nowiki># Maintainer: NAME <EMAIL>
# 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}\"" \
    .
}}


pkgname=PACKAGE NAME
==== Flag meaning ====
pkgver=1.2.3
pkgrel=1
pkgdesc="PACKAGE DESCRIPTION"
arch=('x86_64' 'i686')
url="http://SERVER/$pkgname/"
license=('GPL3')
makedepends=('go')
options=('!strip' '!emptydirs')
source=("http://SERVER/$pkgname/$pkgname.go")
sha256sums=('00112233445566778899aabbccddeeff')


build() {
* {{ic|1=-buildmode=pie}} enables [[Wikipedia:Position-independent code|PIE compilation]] for binary harderning.
  go build -o "$pkgname"
* {{ic|-trimpath}}  important for [[reproducible builds]] so full build paths and module paths are not embedded.
}
* {{ic|1=-mod=readonly}} ensure the module files are not updated in any go actions.
* {{ic|-modcacherw}} is not important, but it ensures that go modules creates a write-able path. Default is read-only.


package() {
{{Tip|When package sources include a {{ic|vendor}} directory with {{ic|modules.txt}}, the {{ic|-mod}} flag can safely be changed to {{ic|1=-mod=vendor}}.}}
  install -Dm755 "$pkgname" "$pkgdir/usr/bin/$pkgname"
}


# vim:set ts=2 sw=2 et:</nowiki>}}
{{Warning|It is up to the packager to verify the build flags are passed correctly to the compiler. Read the {{ic|Makefile}} if there is one.}}


==== Sample packages ====
==== Supporting debug packages ====
* {{AUR|gorun}}


=== Sample PKGBUILDs for Go libraries that also includes executables ===
Enabling debug packages with source listing and proper symbol look ups require a few modifications to the default buildflags.


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


This is the recommended way, instead of the method below.
The above options should produce a debug package with proper detached symbols and source listings which can then be picked up by the debugger.


Here is a way that relies on go get.
{{bc|1=
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"


You probably will not need to modify the build() or package() functions at all, only the variables at the top (pkgname etc).
go build -ldflags "-compressdwarf=false -linkmode external" .
}}


If this does not work, test with go get first.
=== Output directory ===


{{Note| Remove {{Ic|/...}} if the PKGBUILD fails!}}
There are currently a few ways to build all go binaries in a project.


{{bc|<nowiki># Maintainer: NAME <EMAIL>
{{bc|
build(){
    cd "$pkgname-$pkgver"
    go build -o output-binary .
}
}}


pkgname=codesearch
{{ic|...}} is a shorthand for the compiler to recursively descend into the directory and find all binaries. It can be used in conjunction with a output directory to build everything.
pkgver=20120515
pkgrel=1
pkgdesc="Code indexing and search written in Go"
arch=('x86_64' 'i686')
url="https://github.com/google/codesearch"
license=('BSD')
depends=('go')
makedepends=('mercurial')
options=('!strip' '!emptydirs')
_gourl=github.com/google/codesearch


build() {
{{bc|
  GOPATH="$srcdir" go get -fix -v -x ${_gourl}/...
prepare(){
    cd "$pkgname-$pkgver"
    mkdir -p build
}
}


check() {
build(){
  GOPATH="$GOPATH:$srcdir" go test -v -x ${_gourl}/...
    cd "$pkgname-$pkgver"
    go build -o build ./cmd/...
}
}
}}


package() {
== Sample PKGBUILD ==
  mkdir -p "$pkgdir/usr/bin"
  install -p -m755 "$srcdir/bin/"* "$pkgdir/usr/bin"


  mkdir -p "$pkgdir/$GOPATH"
{{bc|<nowiki>
  cp -Rv --preserve=timestamps "$srcdir/"{src,pkg} "$pkgdir/$GOPATH"
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')


  # Package license (if available)
prepare(){
   for f in LICENSE COPYING LICENSE.* COPYING.*; do
   cd "$pkgname-$pkgver"
    if [ -e "$srcdir/src/$_gourl/$f" ]; then
  mkdir -p build/
      install -Dm644 "$srcdir/src/$_gourl/$f" \
        "$pkgdir/usr/share/licenses/$pkgname/$f"
    fi
  done
}
}
# vim:set ts=2 sw=2 et:</nowiki>}}
Thanks to Rémy Oudompheng‎ for this one.
==== Using ''go get'' ====
Here is another way that relies on {{Ic|go get}}.
You probably will not need to modify the build() or package() functions at all, only the variables at the top (pkgname etc).
If this does not work, test with {{Ic|go get}} first.
{{bc|<nowiki># Maintainer: NAME <EMAIL>
pkgname=PACKAGE NAME
pkgver=1.2.3
pkgrel=1
pkgdesc="PACKAGE DESCRIPTION"
arch=('x86_64' 'i686')
url="http://SERVER/$pkgname/"
license=('MIT')
makedepends=('go' 'git')
options=('!strip' '!emptydirs')
_gourl=SERVER.NET/PATH/MODULENAME


build() {
build() {
   export GOROOT=/usr/lib/go
  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/...
}


  rm -rf build
check() {
  mkdir -p build/go
   cd "$pkgname-$pkgver"
   cd build/go
   go test ./...
 
  for f in "$GOROOT/"*; do
    ln -s "$f"
   done
 
  rm pkg
  mkdir pkg
  cd pkg
 
  for f in "$GOROOT/pkg/"*; do
    ln -s "$f"
  done
 
  platform=`for f in "$GOROOT/pkg/"*; do echo \`basename $f\`; done|grep linux`
 
  rm -f "$platform"
  mkdir "$platform"
  cd "$platform"
 
  for f in "$GOROOT/pkg/$platform/"*.h; do
    ln -s "$f"
  done
 
  export GOROOT="$srcdir/build/go"
  export GOPATH="$srcdir/build"
 
  go get -fix "$_gourl"
 
  # Prepare executable
  if [ -d "$srcdir/build/src" ]; then
    cd "$srcdir/build/src/$_gourl"
    go build -o "$srcdir/build/$pkgname"
  else
    echo 'Old sources for a previous version of this package are already present!'
    echo 'Build in a chroot or uninstall the previous version.'
    return 1
  fi
}
}


package() {
package() {
   export GOROOT="$GOPATH"
   cd "$pkgname-$pkgver"
 
   install -Dm755 build/$pkgname "$pkgdir"/usr/bin/$pkgname
  # Package go package files
}
  for f in "$srcdir/build/go/pkg/"* "$srcdir/build/pkg/"*; do
</nowiki>}}
    # If it's a directory
    if [ -d "$f" ]; then
      cd "$f"
      mkdir -p "$pkgdir/$GOROOT/pkg/`basename $f`"
      for z in *; do
        # Check if the directory name matches
        if [ "$z" == `echo $_gourl | cut -d/ -f1` ]; then
          cp -r $z "$pkgdir/$GOROOT/pkg/`basename $f`"
        fi
      done
      cd ..
    fi
   done
 
  # Package source files
  if [ -d "$srcdir/build/src" ]; then
    mkdir -p "$pkgdir/$GOROOT/src/pkg"
    cp -r "$srcdir/build/src/"* "$pkgdir/$GOROOT/src/pkg/"
    find "$pkgdir" -depth -type d -name .git -exec rm -r {} \;
  fi


  # Package license (if available)
== Example packages ==
  for f in LICENSE COPYING; do
    if [ -e "$srcdir/build/src/$_gourl/$f" ]; then
      install -Dm644 "$srcdir/build/src/$_gourl/$f" \
        "$pkgdir/usr/share/licenses/$pkgname/$f"
    fi
  done
 
  # Package executables
  if [ -e "$srcdir/build/$pkgname" ]; then
    install -Dm755 "$srcdir/build/$pkgname" \
      "$pkgdir/usr/bin/$pkgname"
  fi
}


# vim:set ts=2 sw=2 et:</nowiki>}}
* {{Pkg|podman}}
* {{Pkg|k9s}}
* {{Pkg|helm}}

Latest revision as of 11:40, 18 June 2023

Arch package guidelines

32-bitCLRCMakeCrossDKMSEclipseElectronFontFree PascalGNOMEGoHaskellJavaKDEKernelLispMesonMinGWNode.jsNonfreeOCamlPerlPHPPythonRRubyRustShellVCSWebWine

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

General guidelines

Package naming

Use go-modulename 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.

Building

Dependencies

Go 1.11 introduced the initial support for go modules. This allows Go upstream code to declare dependencies and pin them to the given project version. Currently our packaging efforts utilize this to vendor dependencies.

Upstream project without go modules

For upstream code that does not utilise Go modules, the following workaround exists. Consider filing an issue upstream.

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
}
Note: Any such hacks makes the package unreproducible as modules will change between package builds.

Flags and build options

Most Makefiles written for go applications do not respect the build flags provided by build systems along with overwriting GOFLAGS, this causes Go binaries to not be compiled with RELRO as we need CGO_CFLAGS and CGO_LDFLAGS to be set for the compiler. This needs to be patched into the Makefile, or the Makefile should be omitted.

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}\"" \
    .

Flag meaning

  • -buildmode=pie enables PIE compilation for binary harderning.
  • -trimpath important for reproducible builds so full build paths and module paths are not embedded.
  • -mod=readonly ensure the module files are not updated in any go actions.
  • -modcacherw is not important, but it ensures that go modules creates a write-able path. Default is read-only.
Tip: When package sources include a vendor directory with modules.txt, the -mod flag can safely be changed to -mod=vendor.
Warning: It is up to the packager to verify the build flags are passed correctly to the compiler. Read the Makefile if there is one.

Supporting debug packages

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" .

Output directory

There are currently a few ways to build all go binaries in a project.

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

... is a shorthand for the compiler to recursively descend into the directory and find all binaries. It can be used in conjunction with a output directory to build everything.

prepare(){
    cd "$pkgname-$pkgver"
    mkdir -p build
}

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

Sample 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
}

Example packages