Go in Chromium OS

This page provides information on using Go in Chromium OS, including recommendations for project organization, importing and managing third party packages, and writing ebuilds.

Recommended organization of code for Go projects in Chromium OS

Projects should generally follow the official recommendation to organize their Go workspaces: golang.org/doc/code.html
These are some additional recommendations and naming conventions for organizing Go project workspaces in Chromium OS:
  • If the Go project is in its own git repository, it should be set up such that the root of the repository is a workspace.
    • It should have a single directory path "src/<project name>/" at the top level of the repository.
    • This way, import paths for packages in the project always begin with "<project name>/".

  • If the project provides useful Go library packages for other projects to import, put all such packages under "src/chromiumos/".
    • This way, import paths for Chromium OS specific common Go packages always begin with "chromiumos/" prefix.

  • When adding Go code to an existing project already containing code in other languages, create a top level directory called "go" in the repository and use that as the Go workspace.
    • Follow the rest of the recommendation for new projects above (e.g. all code should be under "go/src/<project>/" or "go/src/chromiumos/").

  • IMPORTANT: Do not use "go get" or vendor third party packages to individual project repositories.
    • Instead, mirror external repositories to the Chromium Git server and write an ebuild (in "chromiumos-overlay/dev-go") that installs the third party packages to "/usr/lib/gopath".

Recommendations for local development and builds

Set the GOPATH environment variable to contain two elements:
  1. Path to the local Go workspace (directory containing "src/<project>/").
  2. Path to all locally installed (emerged) Go packages: "/usr/lib/gopath".

When compiling for host, invoke the Go command as "x86_64-pc-linux-gnu-go", or simply, "go".
  • The host compiler is included in the chromium-os-sdk and is installed automatically when a chroot is created.
  • A few useful utilities like "gofmt", "godoc", "golint", and "goguru" are also in the SDK and installed in the chroot.
    • "repo upload" is configured to warn when trying to upload a change containing any Go files not formatted with gofmt.

Go cross compiler for a board is installed automatically when "setup_board" is run in the chroot.
  • The cross-compiling Go command is determined by the toolchain target tuple for the board:
    • Use "x86_64-cros-linux-gnu-go" for amd64 targets (e.g. "panther").
    • Use "armv7a-cros-linux-gnueabi-go" for arm targets (e.g. "daisy").
  • The cross compilers are configured to enable full Cgo support (i.e. they automatically use the correct cross-gcc for compiling the C parts).

Setting up ebuilds for third party Go packages

IMPORTANT: Mirror third party repositories on Chromium Git.
Write an ebuild to fetch and install third party packages to "/usr/lib/gopath".
  • If the upstream repository has tagged releases, use the ebuild version to fetch and install the corresponding release. Example: dev-go/fsnotify/fsnotify-1.3.1.ebuild
    # Copyright 2016 The Chromium OS Authors. All rights reserved. # Distributed under the terms of the GNU General Public License v2. EAPI=5 # Disable cros-workon auto-uprev since this is an external package. # Use ebuild version to checkout the corresponding tag. CROS_WORKON_BLACKLIST="1" CROS_WORKON_COMMIT="v${PV}" CROS_WORKON_PROJECT="external/github.com/fsnotify/fsnotify" CROS_WORKON_DESTDIR="${S}/src/github.com/fsnotify/fsnotify" CROS_GO_PACKAGES=( "github.com/fsnotify/fsnotify" ) inherit cros-workon cros-go DESCRIPTION="File system notifications for Go" HOMEPAGE="https://github.com/fsnotify/fsnotify" LICENSE="BSD-Google" SLOT="0" KEYWORDS="*" IUSE="" RESTRICT="binchecks strip" DEPEND="" RDEPEND="dev-go/go-sys"
  • If the upstream repository does not have release tags, keep the ebuild version at 0.0.1 and manage the commit hash manually. Example: dev-go/glog/glog-0.0.1.ebuild
    # Copyright 2015 The Chromium OS Authors. All rights reserved. # Distributed under the terms of the GNU General Public License v2. EAPI=5 # Disable cros-workon auto-uprev since this is an external package. # Must manage commit hash manually. CROS_WORKON_BLACKLIST="1" CROS_WORKON_COMMIT="44145f04b68cf362d9c4df2182967c2275eaefed" CROS_WORKON_PROJECT="external/github.com/golang/glog" CROS_WORKON_DESTDIR="${S}/src/github.com/golang/glog" CROS_GO_PACKAGES=( "github.com/golang/glog" ) inherit cros-workon cros-go DESCRIPTION="Leveled execution logs for Go" HOMEPAGE="https://github.com/golang/glog" LICENSE="BSD-Google" SLOT="0" KEYWORDS="*" IUSE="" RESTRICT="binchecks strip" DEPEND="" RDEPEND=""
  • Use CROS_WORKON_DESTDIR to place the imported package tree at the appropriate location inside "${S}".
  • Use CROS_GO_PACKAGES to list the packages to install, and inherit eclass "cros-go".
  • The installed packages can now be imported using their usual import paths, i.e.
    import "github.com/golang/glog"

A single ebuild can install any combination of packages from multiple upstream repositories:
  • CROS_WORKON_PROJECT, CROS_WORKON_COMMIT, and CROS_WORKON_DESTDIR variables can be arrays, and can be used to construct a combined source tree from multiple repositories under "${S}".
  • Bundling related packages together in a self contained group installed by a single ebuild can make project dependencies simpler.
  • Use common sense and judgement. Resist monolithic ebuilds that install dozens of packages from as many repositories. On the other extreme, do not write separate ebuilds to install different packages from the same repository.

Setting up ebuilds for Chromium OS Go projects

IMPORTANT: Never use "go get" from an ebuild. Instead, write ebuilds for external dependencies and let Portage make them available under "/usr/lib/gopath".

If a project is only providing common "chromiumos/" Go packages for use by other projects, its ebuild only needs to fetch and install the package files to "/usr/lib/gopath".
  • Example: dev-go/seccomp/seccomp-9999.ebuild
    # Copyright 2015 The Chromium OS Authors. All rights reserved. # Distributed under the terms of the GNU General Public License v2. EAPI=5 CROS_WORKON_PROJECT="chromiumos/platform/go-seccomp" CROS_WORKON_LOCALNAME="../platform/go-seccomp" CROS_GO_PACKAGES=( "chromiumos/seccomp" ) inherit cros-workon cros-go DESCRIPTION="Go support for Chromium OS Seccomp-BPF policy files" HOMEPAGE="https://chromium.org/chromium-os/developer-guide/chromium-os-sandboxing" LICENSE="BSD-Google" SLOT="0" KEYWORDS="~*" IUSE="" RESTRICT="binchecks strip" DEPEND="" RDEPEND=""
    • The seccomp package is located in "chromiumos/platform/go-seccomp" repo at "src/chromiumos/seccomp/".
    • This package can now be imported by other projects using
      import "chromiumos/seccomp"
  • Note that a single ebuild can install multiple related packages inside "/usr/lib/gopath/src/chromiumos/...".
  • Also, multiple ebuilds can install packages inside "/usr/lib/gopath/src/chromiumos/..." as long as there's no conflict for package paths.
  • Use RDEPEND to add other ebuilds that provide dependencies that the packages in this ebuild import. This will ensure that those other dependencies are also made available and remain available as long as the packages from this ebuild are installed in "/usr/lib/gopath".

For a typical Go project that needs to build and install executables:
  • Example: dev-go/golint/golint-0.0.1.ebuild
    # Copyright 2015 The Chromium OS Authors. All rights reserved. # Distributed under the terms of the GNU General Public License v2. EAPI=5 # Disable cros-workon auto-uprev since this is an external package. # Must manage commit hash manually. CROS_WORKON_BLACKLIST="1" CROS_WORKON_COMMIT="3390df4df2787994aea98de825b964ac7944b817" CROS_WORKON_PROJECT="external/github.com/golang/lint" CROS_WORKON_DESTDIR="${S}/src/github.com/golang/lint" CROS_GO_BINARIES=( "github.com/golang/lint/golint" ) inherit cros-workon cros-go DESCRIPTION="A linter for Go source code" HOMEPAGE="https://github.com/golang/lint" LICENSE="BSD-Google" SLOT="0" KEYWORDS="*" IUSE="" RESTRICT="binchecks strip" DEPEND="dev-go/go-tools" RDEPEND=""
  • If the project needs to import packages from outside its own repository, list those dependencies in the DEPEND variable (but not in RDEPEND, as these are strictly build time only dependencies).
  • Always specify the binaries using "CROS_GO_BINARIES" variable. This will pick the correct compiler for cross-compiling, and invoke it with appropriate GOPATH and flags. (e.g. "emerge-daisy" will automatically use the arm cross-compiler, import packages from "/build/daisy/usr/lib/gopath", and build PIE binaries).

A single ebuild can install executable binaries, as well as provide Go packages for other projects to import.
  • Example: dev-go/go-tools/go-tools-0.0.1.ebuild
    # Copyright 2015 The Chromium OS Authors. All rights reserved. # Distributed under the terms of the GNU General Public License v2. EAPI=5 # Disable cros-workon auto-uprev since this is an external package. # Must manage commit hash manually. CROS_WORKON_BLACKLIST="1" CROS_WORKON_COMMIT="0db92ca630c08f00e3ba4b5abea93836ca04b42e" CROS_WORKON_REPO="https://go.googlesource.com" CROS_WORKON_PROJECT="tools" CROS_WORKON_DESTDIR="${S}/src/golang.org/x/tools" CROS_GO_PACKAGES=( "golang.org/x/tools/go/gcimporter15" ) CROS_GO_TEST=( "${CROS_GO_PACKAGES[@]}" ) CROS_GO_BINARIES=( "golang.org/x/tools/cmd/godoc" "golang.org/x/tools/cmd/guru:goguru" ) inherit cros-workon cros-go DESCRIPTION="Packages and tools that support the Go programming language" HOMEPAGE="https://golang.org/x/tools" LICENSE="BSD-Google" SLOT="0" KEYWORDS="*" IUSE="" RESTRICT="binchecks strip" DEPEND="" RDEPEND=""
    • This builds and installs the "godoc" and "goguru" tools. It also provides package "golang.org/x/tools/go/gcimporter15" required to build "dev-go/golint" above.
  • Use the DEPEND/RDEPEND variables carefully in a combination ebuild like this one:
    • If a dependency package is needed to build the executables, put it in DEPEND.
    • If a dependency package is imported by the Go packages installed by this ebuild, put it in RDEPEND.
    • If a dependency package is imported by both the executables built by this ebuild and the Go packages installed by this ebuild, put it in both DEPEND and RDEPEND.

Location of ebuilds and repositories

  • Ebuilds for third party Go packages and tools should go in "chromiumos-overlay/dev-go". Their repositories should be mirrored under "chromium.googlesource.com/external/".
  • Ebuilds for Chromium OS specific common Go packages (import paths beginning with "chromiumos/") should also go to "chromiumos-overlay/dev-go". Their repositories should go to an appropriate place in the tree (e.g. "chromium.googlesource.com/chromiumos/platform/go-seccomp").
  • For other Chromium OS Go projects, use "chromiumos-overlay/dev-go" only if it's a generic helper tool or utility for development. Otherwise, pick a more appropriate category and overlay for the project.

Useful functions and variables

The cros-go eclass is defined here: chromiumos-overlay/eclass/cros-go.eclass
CROS_GO_WORKSPACE
Path to the Go workspace, default is "${S}". Set this variable if the workspace is not at the top of the repository. For example, if all Go packages in the repository are under "go/src/<project>", set this variable to "${S}/go".

CROS_GO_BINARIES
Go executable binaries to build and install. Package paths are relative to "${CROS_GO_WORKSPACE}/src". Each path must contain a package "main". The last component of the package path will become the name of the executable. The executable name can be overridden by appending a colon to the package path, followed by an alternate name. For example:
CROS_GO_BINARIES=( "golang.org/x/tools/cmd/godoc" "golang.org/x/tools/cmd/guru:goguru" )
will build and install "godoc" and "goguru" binaries.

CROS_GO_PACKAGES
Go packages to install in "/usr/lib/gopath". Package paths are relative to "${CROS_GO_WORKSPACE}/src". Packages are installed in "/usr/lib/gopath" such that they can be imported later from Go code using the exact paths listed here. For example:
CROS_GO_PACKAGES=( "github.com/golang/glog" )
will install package files from "${CROS_GO_WORKSPACE}/src/github.com/golang/glog" to "/usr/lib/gopath/src/github.com/golang/glog" and other Go projects can use the package with
import "github.com/golang/glog"

CROS_GO_TEST
Go packages to test. Package paths are relative to "${CROS_GO_WORKSPACE}/src". Package tests are always built and run locally on host. Default is to test all packages in ${CROS_GO_WORKSPACE}.

cros_go
Wrapper function for invoking the Go tool from an ebuild. It provides the following functionality:
  • The correct cross-compiler is automatically detected and used. For example, when running "emerge-daisy", "armv7a-cros-linux-gnueabi-go" will be used to build Go packages and binaries.
  • The GOPATH variable is setup automatically. For example, when running "emerge-daisy", Go packages will be looked up in the local workspace, as well as in "/build/daisy/usr/lib/gopath".
  • When cross-compiling Go packages that use Cgo, the correct C/C++ cross-compilers for the target are automatically detected and used.
For most ebuilds, setting the CROS_GO_BINARIES variable should be enough to build and install Go binaries. Implementation of CROS_GO_BINARIES uses the cros_go wrapper internally.
Comments