| # Copyright 2015-2018 The Chromium OS Authors. All rights reserved. |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| # @ECLASS: cros-go.eclass |
| # @MAINTAINER: |
| # The Chromium OS Authors <chromium-os-dev@chromium.org> |
| # @BUGREPORTS: |
| # Please report bugs via https://crbug.com/new (with component "Tools>ChromeOS-Toolchain") |
| # @VCSURL: https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/master/eclass/@ECLASS@ |
| # @BLURB: Eclass for fetching, building, and installing Go packages. |
| # @DESCRIPTION: |
| # See http://www.chromium.org/chromium-os/developer-guide/go-in-chromium-os for details. |
| |
| # @ECLASS-VARIABLE: CROS_GO_SOURCE |
| # @PRE_INHERIT |
| # @DESCRIPTION: |
| # Path to the upstream repository and commit id. |
| # Go repositories on "github.com" and "*.googlesource.com" are supported. |
| # The source string contains the path of the git repository containing Go |
| # packages, and a commit-id (or version tag). |
| # For example: |
| # CROS_GO_SOURCE="github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed" |
| # will fetch the sources from https://github.com/golang/glog at the |
| # specified commit-id, and |
| # CROS_GO_SOURCE="github.com/pkg/errors v0.8.0" |
| # will fetch the sources from https://github.com/pkg/errors at version |
| # v0.8.0. |
| # By default, the import path for Go packages in the repository is the |
| # same as repository path. This can be overridden by appending a colon |
| # to the repository path, followed by an alternate import path. |
| # For example: |
| # CROS_GO_SOURCE="github.com/go-yaml/yaml:gopkg.in/yaml.v2 v2.2.1" |
| # will fetch the sources from https://github.com/go-yaml/yaml at version |
| # v2.2.1, and install the package under "gopkg.in/yaml.v2". |
| # CROS_GO_SOURCE can contain multiple items when defined as an array: |
| # CROS_GO_SOURCE=( |
| # "github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed" |
| # "github.com/pkg/errors v0.8.0" |
| # "github.com/go-yaml/yaml:gopkg.in/yaml.v2 v2.2.1" |
| # ) |
| |
| # @ECLASS-VARIABLE: CROS_GO_WORKSPACE |
| # @DESCRIPTION: |
| # Path to the Go workspace, default is ${S}. |
| # The Go workspace is searched for packages to build and install. |
| # If all Go packages in a repository are located under "go/src/": |
| # CROS_GO_WORKSPACE="${S}/go" |
| # CROS_GO_WORKSPACE can contain multiple items when defined as an array: |
| # CROS_GO_WORKSPACE=( |
| # "${S}" |
| # "${S}/tast-base" |
| # ) |
| |
| # @ECLASS-VARIABLE: CROS_GO_BINARIES |
| # @DESCRIPTION: |
| # Go programs to build and install. |
| # Each program is specified by the path of a directory that |
| # contains a package "main", or a single Go source file which |
| # must also contain the package "main". For directories, the |
| # last component of the package path becomes the name of the |
| # executable. For files, the ".go" suffix is also stripped. |
| # The executable name can be overridden by appending a colon |
| # to the package path, followed by an alternate name. |
| # The install path for an executable can be overridden by |
| # appending a colon to the package path, followed by the |
| # desired install path/name for it. |
| # For example: |
| # CROS_GO_BINARIES=( |
| # "golang.org/x/tools/cmd/godoc" |
| # "golang.org/x/tools/cmd/guru:goguru" |
| # "golang.org/x/tools/cmd/stringer:/usr/local/bin/gostringer" |
| # "golang.org/x/tools/cmd/foo.go" |
| # "golang.org/x/tools/cmd/foo.go:gofoo" |
| # ) |
| # builds and installs "godoc", "goguru", "gostringer", "foo" |
| # and "gofoo" binaries. |
| |
| # Helper function for path and names used in cros-go_src_{compile,install}. |
| # Returns various views of the information in a CROS_GO_BINARIES entry (a |
| # "specification" or "spec") separated by colons. |
| parse_binspec() { |
| local spec="$1" |
| # Split spec at colon into source and override. |
| local source |
| local override |
| IFS=: read source override empty <<<"${spec}" |
| test -z "${empty}" || die "bad CROS_GO_BINARIES entry: \"${spec}\"" |
| local target |
| local installdir |
| if [[ -z "${override}" ]] ; then |
| target="${spec##*/}" |
| # if there is no override, remove .go suffix (if any). |
| target="${target%%.go}" |
| installdir="/usr/bin" |
| else |
| # target is the last component of the override path |
| target="${override##*/}" |
| installdir="${override%/*}" |
| fi |
| # if the source is a single go file, use its full path name |
| if [[ "${source##*.}" == "go" ]]; then |
| source="${S}/src/${source}" |
| fi |
| # there is no colon in any variable, so colon is a safe separator |
| echo "${source}:${target}:${installdir}" |
| } |
| |
| # @ECLASS-VARIABLE: CROS_GO_VERSION |
| # @DESCRIPTION: |
| # Version string to embed in the executable binary. |
| # The variable main.Version is set to this value at build time. |
| # For example: |
| # CROS_GO_VERSION="${PVR}" |
| # will set main.Version string variable to package version and |
| # revision (if any) of the ebuild. |
| |
| # @ECLASS-VARIABLE: CROS_GO_PACKAGES |
| # @DESCRIPTION: |
| # Go packages to install in /usr/lib/gopath. |
| # 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 to |
| # "/usr/lib/gopath/src/github.com/golang/glog" |
| # and other Go projects can use the package with |
| # import "github.com/golang/glog" |
| # If the last component of a package path is "...", it is |
| # expanded to include all Go packages under the directory. |
| |
| # @ECLASS-VARIABLE: CROS_GO_TEST |
| # @DESCRIPTION: |
| # Go packages to test. |
| # Package tests are run with "-short" flag by default. |
| # Package tests are always built and run locally on host. |
| # Default is to test all packages in CROS_GO_WORKSPACE(s). |
| : ${CROS_GO_TEST:=./...} |
| |
| # @ECLASS-VARIABLE: CROS_GO_VET |
| # @DESCRIPTION: |
| # Go packages to check using "go vet". |
| # As in CROS_GO_PACKAGES, "..." is expanded. |
| |
| # @ECLASS-VARIABLE: CROS_GO_VET_FLAGS |
| # @DESCRIPTION: |
| # Flags to pass to "go vet" if CROS_GO_VET is set. |
| # See https://golang.org/cmd/vet/ for available flags. |
| |
| inherit toolchain-funcs |
| |
| DEPEND="dev-lang/go" |
| |
| # @FUNCTION: cros-go_get |
| # @USAGE: <source> [variables to extract] |
| # @INTERNAL |
| # @DESCRIPTION: |
| # Parse source string and extract different components. |
| # This function parses the string containing upstream |
| # repository, import path, and commit id information |
| # (see description of CROS_GO_SOURCE format above). |
| # It can also be used to construct the name of the |
| # distfile and a uri for fetching it. |
| cros-go_get() { |
| local src commit repopath importpath distfile uri |
| src="$1" |
| commit="${src##* }" |
| repopath="${src%% *}" |
| importpath="${repopath#*:}" |
| repopath="${repopath%:*}" |
| distfile="${repopath//\//-}-${commit}.tar.gz" |
| uri="https://${repopath}/${commit}.tar.gz" |
| case "${repopath%%/*}" in |
| github.com) |
| uri="https://${repopath}/archive/${commit}.tar.gz" ;; |
| *.googlesource.com) |
| uri="https://${repopath}/+archive/${commit}.tar.gz" ;; |
| *) |
| die "Unsupported upstream source in ${repopath}" ;; |
| esac |
| |
| shift |
| local arg |
| for arg in "$@" ; do |
| case "${arg}" in |
| commit) printf "%s" "${commit}" ;; |
| repopath) printf "%s" "${repopath}" ;; |
| importpath) printf "%s" "${importpath}" ;; |
| distfile) printf "%s" "${distfile}" ;; |
| uri) printf "%s" "${uri}" ;; |
| *) printf "${arg}" ;; |
| esac |
| done |
| } |
| |
| # @FUNCTION: cros-go_src_uri |
| # @RETURN: a valid SRC_URI for CROS_GO_SOURCE |
| # @DESCRIPTION: |
| # Set the SRC_URI in an ebuild with: |
| # SRC_URI="$(cros-go_src_uri)" |
| cros-go_src_uri() { |
| local src |
| for src in "${CROS_GO_SOURCE[@]}" ; do |
| cros-go_get "${src}" uri " -> " distfile "\n" |
| done |
| } |
| |
| # @FUNCTION: cros-go_pkg_nofetch |
| # @DESCRIPTION: |
| # Print useful information on how to download a source tarball and |
| # add it to chromeos-localmirror. |
| cros-go_pkg_nofetch() { |
| local src |
| for src in "${CROS_GO_SOURCE[@]}" ; do |
| local uri="$(cros-go_get "${src}" uri)" |
| local distfile="$(cros-go_get "${src}" distfile)" |
| einfo "Run these commands to add ${distfile} to chromeos-localmirror:" |
| einfo " wget -O ${distfile} ${uri}" |
| einfo " gsutil cp -a public-read ${distfile} gs://chromeos-localmirror/distfiles/" |
| einfo |
| done |
| einfo "After all distfiles have been mirrored, update the 'Manifest' file with:" |
| einfo " ebuild ${EBUILD} manifest" |
| } |
| |
| # @FUNCTION: cros-go_src_unpack |
| # @DESCRIPTION: |
| # Unpack the source tarball under appropriate location based on |
| # the desired import path. |
| cros-go_src_unpack() { |
| local src |
| for src in "${CROS_GO_SOURCE[@]}" ; do |
| local commit="$(cros-go_get "${src}" commit)" |
| local repopath="$(cros-go_get "${src}" repopath)" |
| local importpath="$(cros-go_get "${src}" importpath)" |
| local distfile="$(cros-go_get "${src}" distfile)" |
| |
| local destdir="${S}/src/${importpath}" |
| case "${repopath%%/*}" in |
| github.com) |
| # Unpacking tarballs from github creates a top level |
| # directory "projectname-version", so extra logic is |
| # required to make the contents appear correctly in |
| # the desired destination directory. |
| mkdir -p "${destdir%/*}" || die |
| pushd "${destdir%/*}" >/dev/null || die |
| unpack "${distfile}" |
| mv "${repopath##*/}-${commit#v}" "${importpath##*/}" || die |
| popd >/dev/null || die |
| ;; |
| *.googlesource.com) |
| mkdir -p "${destdir}" || die |
| pushd "${destdir}" >/dev/null || die |
| unpack "${distfile}" |
| popd >/dev/null || die |
| ;; |
| esac |
| done |
| } |
| |
| # @FUNCTION: cros-go_workspace |
| # @RETURN: Go workspaces, colon separated |
| # @INTERNAL |
| # @DESCRIPTION: |
| # Return the list of workspaces in CROS_GO_WORKSPACE, |
| # properly formatted for inclusion into GOPATH. |
| cros-go_workspace() { |
| if [[ ${#CROS_GO_WORKSPACE[@]} != 0 ]] ; then |
| ( IFS=:; echo "${CROS_GO_WORKSPACE[*]}" ) |
| else |
| echo "${S}" |
| fi |
| } |
| |
| # @FUNCTION: cros-go_gopath |
| # @RETURN: a valid GOPATH for CROS_GO_WORKSPACE |
| # @DESCRIPTION: |
| # Set the GOPATH in an ebuild with: |
| # GOPATH="$(cros-go_gopath)" |
| cros-go_gopath() { |
| echo "$(cros-go_workspace):${SYSROOT}/usr/lib/gopath" |
| } |
| |
| # @FUNCTION: cros_go |
| # @DESCRIPTION: |
| # Wrapper function for invoking the Go tool from an ebuild. |
| # Sets up GOPATH, and uses the appropriate cross-compiler. |
| cros_go() { |
| GOPATH="$(cros-go_gopath)" $(tc-getGO) "$@" || die |
| } |
| |
| # @FUNCTION: go_list |
| # @DESCRIPTION: |
| # List all Go packages matching a pattern. |
| # Only list packages in the current workspace. |
| go_list() { |
| GOPATH="$(cros-go_workspace)" $(tc-getGO) list "$@" || die |
| } |
| |
| # @FUNCTION: go_test |
| # @DESCRIPTION: |
| # Wrapper function for building and running unit tests. |
| # Package tests are always built and run locally on host. |
| go_test() { |
| GOPATH="$(cros-go_gopath)" $(tc-getBUILD_GO) test "$@" || die |
| } |
| |
| # @FUNCTION: go_vet |
| # @DESCRIPTION: |
| # Wrapper function for running "go vet". |
| go_vet() { |
| GOPATH="$(cros-go_gopath)" $(tc-getBUILD_GO) vet \ |
| "${CROS_GO_VET_FLAGS[@]}" "$@" || die |
| } |
| |
| # @FUNCTION: cros-go_src_compile |
| # @DESCRIPTION: |
| # Build CROS_GO_BINARIES. |
| cros-go_src_compile() { |
| local bin |
| local source |
| local target |
| local installdir |
| for bin in "${CROS_GO_BINARIES[@]}" ; do |
| einfo "Building \"${bin}\"" |
| IFS=: read source target installdir <<<"$(parse_binspec "${bin}")" |
| cros_go build -v \ |
| ${CROS_GO_VERSION:+"-ldflags=-X main.Version=${CROS_GO_VERSION}"} \ |
| -o "${target}" \ |
| "${source}" |
| done |
| |
| local pkg |
| for pkg in "${CROS_GO_VET[@]}" ; do |
| einfo "Vetting \"${pkg}\"" |
| go_vet "${pkg}" |
| done |
| } |
| |
| # @FUNCTION: cros-go_src_test |
| # @DESCRIPTION: |
| # Run tests for packages listed in CROS_GO_TEST. |
| cros-go_src_test() { |
| local pkglist=( $(go_list "${CROS_GO_TEST[@]}") ) |
| go_test -short "${pkglist[@]}" |
| } |
| |
| # @FUNCTION: cros-go_src_install |
| # @DESCRIPTION: |
| # Install CROS_GO_BINARIES and CROS_GO_PACKAGES. |
| cros-go_src_install() { |
| # Install the compiled binaries. |
| local bin |
| local source |
| local target |
| local installdir |
| for bin in "${CROS_GO_BINARIES[@]}" ; do |
| einfo "Installing \"${bin}\"" |
| IFS=: read source target installdir <<<"$(parse_binspec "${bin}")" |
| ( |
| # Run in sub-shell so we do not modify env. |
| exeinto "${installdir}" |
| doexe "${target}" |
| ) |
| done |
| |
| # Install the importable packages in /usr/lib/gopath. |
| local pkglist=() |
| if [[ ${#CROS_GO_PACKAGES[@]} != 0 ]] ; then |
| pkglist=( $(go_list "${CROS_GO_PACKAGES[@]}") ) |
| fi |
| local pkg |
| for pkg in "${pkglist[@]}" ; do |
| einfo "Installing \"${pkg}\"" |
| local pkgdir="$(go_list -f "{{.Dir}}" "${pkg}")" |
| ( |
| # Run in sub-shell so we do not modify env. |
| insinto "/usr/lib/gopath/src/${pkg}" |
| local file |
| while read -d $'\0' -r file ; do |
| doins "${file}" |
| done < <(find "${pkgdir}" -maxdepth 1 ! -type d -print0) |
| ) |
| done |
| } |
| |
| # @FUNCTION: cros-go_pkg_postinst |
| # @DESCRIPTION: |
| # Check for missing dependencies of installed packages. |
| cros-go_pkg_postinst() { |
| # This only works if we're building and installing from source. |
| [[ "${MERGE_TYPE}" == "source" ]] || return |
| |
| # Get the list of packages from the workspace in ${S}. |
| local pkglist=() |
| if [[ ${#CROS_GO_PACKAGES[@]} != 0 ]] ; then |
| pkglist=( $(go_list "${CROS_GO_PACKAGES[@]}") ) |
| fi |
| |
| # Switch the workspace to where the packages were installed. |
| local CROS_GO_WORKSPACE="${SYSROOT}/usr/lib/gopath" |
| |
| # For each installed package, check for missing dependencies. |
| local pkg |
| for pkg in "${pkglist[@]}" ; do |
| if [[ $(go_list -f "{{.Incomplete}}" "${pkg}") == "true" ]] ; then |
| go_list -f "{{.DepsErrors}}" "${pkg}" |
| die "Package has missing dependency: \"${pkg}\"" |
| fi |
| done |
| } |
| |
| if [[ ${#CROS_GO_SOURCE[@]} != 0 ]] ; then |
| EXPORT_FUNCTIONS pkg_nofetch src_unpack |
| fi |
| |
| EXPORT_FUNCTIONS src_compile src_test src_install pkg_postinst |