Gentoo Archives: gentoo-commits

From: William Hubbs <williamh@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: eclass/
Date: Wed, 04 Mar 2020 21:42:21
Message-Id: 1583358050.44b77ebef911630c5302b25bc708954738b35b56.williamh@gentoo
1 commit: 44b77ebef911630c5302b25bc708954738b35b56
2 Author: William Hubbs <williamh <AT> gentoo <DOT> org>
3 AuthorDate: Wed Mar 4 21:40:50 2020 +0000
4 Commit: William Hubbs <williamh <AT> gentoo <DOT> org>
5 CommitDate: Wed Mar 4 21:40:50 2020 +0000
6 URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=44b77ebe
7
8 go-module.eclass: add support for EGO_SUM
9
10 The EGO_SUM variable replaces EGO_VENDOR for go modules.
11
12 Signed-off-by: William Hubbs <williamh <AT> gentoo.org>
13
14 eclass/go-module.eclass | 366 ++++++++++++++++++++++++++++++++++++++++++------
15 1 file changed, 326 insertions(+), 40 deletions(-)
16
17 diff --git a/eclass/go-module.eclass b/eclass/go-module.eclass
18 index 80ff2902b3a..68a72d12e1b 100644
19 --- a/eclass/go-module.eclass
20 +++ b/eclass/go-module.eclass
21 @@ -4,28 +4,29 @@
22 # @ECLASS: go-module.eclass
23 # @MAINTAINER:
24 # William Hubbs <williamh@g.o>
25 +# @AUTHOR:
26 +# William Hubbs <williamh@g.o>
27 +# Robin H. Johnson <robbat2@g.o>
28 # @SUPPORTED_EAPIS: 7
29 # @BLURB: basic eclass for building software written as go modules
30 # @DESCRIPTION:
31 -# This eclass provides basic settings and functions
32 -# needed by all software written in the go programming language that uses
33 -# go modules.
34 -#
35 -# You will know the software you are packaging uses modules because
36 -# it will have files named go.sum and go.mod in its top-level source
37 -# directory. If it does not have these files, use the golang-* eclasses.
38 +# This eclass provides basic settings and functions needed by all software
39 +# written in the go programming language that uses modules.
40 #
41 -# If it has these files and a directory named vendor in its top-level
42 -# source directory, you only need to inherit the eclass since upstream
43 -# is vendoring the dependencies.
44 +# If the software you are packaging has a file named go.mod in its top
45 +# level directory, it uses modules and your ebuild should inherit this
46 +# eclass. If it does not, your ebuild should use the golang-* eclasses.
47 #
48 -# If it does not have a vendor directory, you should use the EGO_VENDOR
49 -# variable and the go-module_vendor_uris function as shown in the
50 -# example below to handle dependencies.
51 +# If, besides go.mod, your software has a directory named vendor in its
52 +# top level directory, the only thing you need to do is inherit the
53 +# eclass. If there is no vendor directory, you need to also populate
54 +# EGO_SUM and call go-module_set_globals as discussed below.
55 #
56 # Since Go programs are statically linked, it is important that your ebuild's
57 # LICENSE= setting includes the licenses of all statically linked
58 # dependencies. So please make sure it is accurate.
59 +# You can use a utility like dev-util/golicense (network connectivity is
60 +# required) to extract this information from the compiled binary.
61 #
62 # @EXAMPLE:
63 #
64 @@ -33,19 +34,21 @@
65 #
66 # inherit go-module
67 #
68 -# EGO_VENDOR=(
69 -# "github.com/xenolf/lego 6cac0ea7d8b28c889f709ec7fa92e92b82f490dd"
70 -# "golang.org/x/crypto 453249f01cfeb54c3d549ddb75ff152ca243f9d8 github.com/golang/crypto"
71 +# EGO_SUM=(
72 +# "github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod"
73 +# "github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59"
74 # )
75 #
76 +# go-module_set_globals
77 +#
78 # SRC_URI="https://github.com/example/${PN}/archive/v${PV}.tar.gz -> ${P}.tar.gz
79 -# $(go-module_vendor_uris)"
80 +# ${EGO_SUM_SRC_URI}"
81 #
82 # @CODE
83
84 case ${EAPI:-0} in
85 7) ;;
86 - *) die "${ECLASS} API in EAPI ${EAPI} not yet established."
87 + *) die "${ECLASS} EAPI ${EAPI} is not supported."
88 esac
89
90 if [[ -z ${_GO_MODULE} ]]; then
91 @@ -64,10 +67,12 @@ export GO111MODULE=on
92 export GOCACHE="${T}/go-build"
93
94 # The following go flags should be used for all builds.
95 -# -mod=vendor stopps downloading of dependencies from the internet.
96 # -v prints the names of packages as they are compiled
97 # -x prints commands as they are executed
98 -export GOFLAGS="-mod=vendor -v -x"
99 +# -mod=readonly do not update go.mod/go.sum but fail if updates are needed
100 +# -mod=vendor use the vendor directory instead of downloading dependencies
101 +export GOFLAGS="-v -x -mod=readonly"
102 +[[ ${#EGO_VENDOR[@]} -gt 0 ]] && GOFLAGS+=" -mod=vendor"
103
104 # Do not complain about CFLAGS etc since go projects do not use them.
105 QA_FLAGS_IGNORED='.*'
106 @@ -77,28 +82,37 @@ RESTRICT="strip"
107
108 EXPORT_FUNCTIONS src_unpack pkg_postinst
109
110 +# @ECLASS-VARIABLE: EGO_SUM
111 +# @DESCRIPTION:
112 +# This is an array based on the go.sum content from inside the target package.
113 +# Each array entry must be quoted and contain information from a single
114 +# line from go.sum.
115 +#
116 +# The format of go.sum is described upstream here:
117 +# https://tip.golang.org/cmd/go/#hdr-Module_authentication_using_go_sum
118 +#
119 +# h1:<hash> is the Hash1 structure used by upstream Go
120 +# Note that Hash1 is MORE stable than Gentoo distfile hashing, and upstream
121 +# warns that it's conceptually possible for the Hash1 value to remain stable
122 +# while the upstream zipfiles change. E.g. it does NOT capture mtime changes in
123 +# files within a zipfile.
124 +
125 # @ECLASS-VARIABLE: EGO_VENDOR
126 # @DESCRIPTION:
127 -# This variable contains a list of vendored packages.
128 -# The items of this array are strings that contain the
129 -# import path and the git commit hash for a vendored package.
130 -# If the import path does not start with github.com, the third argument
131 -# can be used to point to a github repository.
132 +# This variable is deprecated and should no longer be used. Please
133 +# convert your ebuilds to use EGO_SUM.
134
135 # @FUNCTION: go-module_vendor_uris
136 # @DESCRIPTION:
137 -# Convert the information in EGO_VENDOR to a format suitable for
138 -# SRC_URI.
139 -# A call to this function should be added to SRC_URI in your ebuild if
140 -# the upstream package does not include vendored dependencies.
141 +# This function is deprecated.
142 go-module_vendor_uris() {
143 local hash import line repo x
144 for line in "${EGO_VENDOR[@]}"; do
145 read -r import hash repo x <<< "${line}"
146 - if [[ -n $x ]]; then
147 + if [[ -n ${x} ]]; then
148 eerror "Trailing information in EGO_VENDOR in ${P}.ebuild"
149 eerror "${line}"
150 - eerror "Trailing information is: \"$x\""
151 + eerror "Trailing information is: \"${x}\""
152 die "Invalid EGO_VENDOR format"
153 fi
154 : "${repo:=${import}}"
155 @@ -106,18 +120,226 @@ go-module_vendor_uris() {
156 done
157 }
158
159 +# @ECLASS-VARIABLE: _GOMODULE_GOPROXY_BASEURI
160 +# @DESCRIPTION:
161 +# Golang module proxy service to fetch module files from. Note that the module
162 +# proxy generally verifies modules via the Hash1 code.
163 +#
164 +# Users in China may find some mirrors in the default list blocked, and should
165 +# explicitly set an entry in /etc/portage/mirrors for goproxy to
166 +# https://goproxy.cn/ or another mirror that is not blocked in China.
167 +# See https://arslan.io/2019/08/02/why-you-should-use-a-go-module-proxy/ for
168 +# further details
169 +#
170 +# This variable is NOT intended for user-level configuration of mirrors, but
171 +# rather to cover go modules that might exist only on specific Goproxy
172 +# servers for non-technical reasons.
173 +#
174 +# This variable should NOT be present in user-level configuration e.g.
175 +# /etc/portage/make.conf, as it will violate metadata immutability!
176 +#
177 +# I am considering removing this and just hard coding mirror://goproxy
178 +# below, so please do not rely on it.
179 +: "${_GOMODULE_GOPROXY_BASEURI:=mirror://goproxy/}"
180 +
181 +# @ECLASS-VARIABLE: _GOMODULE_GOSUM_REVERSE_MAP
182 +# @DESCRIPTION:
183 +# Mapping back from Gentoo distfile name to upstream distfile path.
184 +# Associative array to avoid O(N*M) performance when populating the GOPROXY
185 +# directory structure.
186 +declare -A -g _GOMODULE_GOSUM_REVERSE_MAP
187 +
188 +# @FUNCTION: go-module_set_globals
189 +# @DESCRIPTION:
190 +# Convert the information in EGO_SUM for other usage in the ebuild.
191 +# - Populates EGO_SUM_SRC_URI that can be added to SRC_URI
192 +# - Exports _GOMODULE_GOSUM_REVERSE_MAP which provides reverse mapping from
193 +# distfile back to the relative part of SRC_URI, as needed for
194 +# GOPROXY=file:///...
195 +go-module_set_globals() {
196 + local line exts
197 + # for tracking go.sum errors
198 + local error_in_gosum=0
199 + local -a gosum_errorlines
200 + # used make SRC_URI easier to read
201 + local newline=$'\n'
202 +
203 + # Now parse EGO_SUM
204 + for line in "${EGO_SUM[@]}"; do
205 + local module version modfile version_modfile kvs x
206 + read -r module version_modfile kvs <<< "${line}"
207 + # kvs contains the hash and may contain other data from
208 + # upstream in the future. We do not currently use any of this data.
209 +
210 + # Split 'v0.3.0/go.mod' into 'v0.3.0' and '/go.mod'
211 + # It might NOT have the trailing /go.mod
212 + IFS=/ read -r version modfile x <<<"${version_modfile}"
213 + # Reject multiple slashes
214 + if [[ -n ${x} ]]; then
215 + error_in_gosum=1
216 + gosum_errorlines+=( "Bad version: ${version_modfile}" )
217 + continue
218 + fi
219 +
220 + # The modfile variable should be either empty or '/go.mod'
221 + # There is a chance that upstream Go might add something else here in
222 + # the future, and we should be prepared to capture it.
223 + # The .info files do not need to be downloaded, they will be created
224 + # based on the .mod file.
225 + # See https://github.com/golang/go/issues/35922#issuecomment-584824275
226 + exts=()
227 + local errormsg=''
228 + case "${modfile}" in
229 + '') exts=( zip ) ;;
230 + 'go.mod'|'/go.mod') exts=( mod ) ;;
231 + *) errormsg="Unknown modfile: line='${line}', modfile='${modfile}'" ;;
232 + esac
233 +
234 + # If it was a bad entry, restart the loop
235 + if [[ -n ${errormsg} ]]; then
236 + error_in_gosum=1
237 + gosum_errorlines+=( "${errormsg} line='${line}', modfile='${modfile}'" )
238 + continue
239 + fi
240 +
241 + _dir=$(_go-module_gomod_encode "${module}")
242 +
243 + for _ext in "${exts[@]}" ; do
244 + # Relative URI within a GOPROXY for a file
245 + _reluri="${_dir}/@v/${version}.${_ext}"
246 + # SRC_URI: LHS entry
247 + _uri="${_GOMODULE_GOPROXY_BASEURI}/${_reluri}"
248 +# _uri="mirror://goproxy/${_reluri}"
249 + # SRC_URI: RHS entry, encode any slash in the path as
250 + # %2F in the filename
251 + _distfile="${_reluri//\//%2F}"
252 +
253 + EGO_SUM_SRC_URI+=" ${_uri} -> ${_distfile}${newline}"
254 + _GOMODULE_GOSUM_REVERSE_MAP["${_distfile}"]="${_reluri}"
255 + done
256 + done
257 +
258 + if [[ ${error_in_gosum} != 0 ]]; then
259 + eerror "Trailing information in EGO_SUM in ${P}.ebuild"
260 + for line in "${gosum_errorlines[@]}" ; do
261 + eerror "${line}"
262 + done
263 + die "Invalid EGO_SUM format"
264 + fi
265 +
266 + # Ensure these variables are not changed past this point
267 + readonly EGO_SUM
268 + readonly EGO_SUM_SRC_URI
269 + readonly _GOMODULE_GOSUM_REVERSE_MAP
270 +
271 + # Set the guard that we are safe
272 + _GO_MODULE_SET_GLOBALS_CALLED=1
273 +}
274 +
275 # @FUNCTION: go-module_src_unpack
276 # @DESCRIPTION:
277 +# - If EGO_VENDOR is set, use the deprecated function to unpack the base
278 +# tarballs and the tarballs indicated in EGO_VENDOR to the correct
279 +# locations.
280 +# - Otherwise, if EGO_SUM is set, unpack the base tarball(s) and set up the
281 +# local go proxy.
282 +# - Otherwise do a normal unpack.
283 +go-module_src_unpack() {
284 + if [[ "${#EGO_VENDOR[@]}" -gt 0 ]]; then
285 + _go-module_src_unpack_vendor
286 + elif [[ "${#EGO_SUM[@]}" -gt 0 ]]; then
287 + _go-module_src_unpack_gosum
288 + else
289 + default
290 + fi
291 +}
292 +
293 +# @FUNCTION: _go-module_src_unpack_gosum
294 +# @DESCRIPTION:
295 +# Populate a GOPROXY directory hierarchy with distfiles from EGO_SUM and
296 +# unpack the base distfiles.
297 +#
298 +# Exports GOPROXY environment variable so that Go calls will source the
299 +# directory correctly.
300 +_go-module_src_unpack_gosum() {
301 + # shellcheck disable=SC2120
302 + debug-print-function "${FUNCNAME}" "$@"
303 +
304 + if [[ ! ${_GO_MODULE_SET_GLOBALS_CALLED} ]]; then
305 + die "go-module_set_globals must be called in global scope"
306 + fi
307 +
308 + local goproxy_dir="${T}/go-proxy"
309 + mkdir -p "${goproxy_dir}" || die
310 +
311 + # For each Golang module distfile, look up where it's supposed to go, and
312 + # symlink into place.
313 + local f
314 + local goproxy_mod_dir
315 + for f in ${A}; do
316 + goproxy_mod_path="${_GOMODULE_GOSUM_REVERSE_MAP["${f}"]}"
317 + if [[ -n "${goproxy_mod_path}" ]]; then
318 + debug-print-function "Populating go proxy for ${goproxy_mod_path}"
319 + # Build symlink hierarchy
320 + goproxy_mod_dir=$( dirname "${goproxy_dir}"/"${goproxy_mod_path}" )
321 + mkdir -p "${goproxy_mod_dir}" || die
322 + ln -sf "${DISTDIR}"/"${f}" "${goproxy_dir}/${goproxy_mod_path}" ||
323 + die "Failed to ln"
324 + local v=${goproxy_mod_path}
325 + v="${v%.mod}"
326 + v="${v%.zip}"
327 + v="${v//*\/}"
328 + _go-module_gosum_synthesize_files "${goproxy_mod_dir}" "${v}"
329 + else
330 + unpack "$f"
331 + fi
332 + done
333 + export GOPROXY="file://${goproxy_dir}"
334 +
335 + # Validate the gosum now
336 + _go-module_src_unpack_verify_gosum
337 +}
338 +
339 +# @FUNCTION: _go-module_gosum_synthesize_files
340 +# @DESCRIPTION:
341 +# Given a path & version, populate all Goproxy metadata files which aren't
342 +# needed to be downloaded directly.
343 +# - .../@v/${version}.info
344 +# - .../@v/list
345 +_go-module_gosum_synthesize_files() {
346 + local target=$1
347 + local version=$2
348 + # 'go get' doesn't care about the hash of the .info files, they
349 + # just need a 'version' element!
350 + # This saves a download of a tiny file
351 + # The .time key is omitted, because that is the time a module was added
352 + # to the upstream goproxy, and not metadata about the module itself.
353 + cat >"${target}/${version}.info" <<-EOF
354 + {
355 + "Version": "${version}",
356 + "shortName": "${version}",
357 + "Name": "${version}"
358 + }
359 + EOF
360 + listfile="${target}"/list
361 + if ! grep -sq -x -e "${version}" "${listfile}" 2>/dev/null; then
362 + echo "${version}" >>"${listfile}"
363 + fi
364 +}
365 +
366 +# @FUNCTION: _go-module_src_unpack_vendor
367 +# @DESCRIPTION:
368 # Extract all archives in ${a} which are not nentioned in ${EGO_VENDOR}
369 # to their usual locations then extract all archives mentioned in
370 # ${EGO_VENDOR} to ${S}/vendor.
371 -go-module_src_unpack() {
372 - debug-print-function ${FUNCNAME} "$@"
373 +_go-module_src_unpack_vendor() {
374 + # shellcheck disable=SC2120
375 + debug-print-function "${FUNCNAME}" "$@"
376 local f hash import line repo tarball vendor_tarballs x
377 vendor_tarballs=()
378 for line in "${EGO_VENDOR[@]}"; do
379 read -r import hash repo x <<< "${line}"
380 - if [[ -n $x ]]; then
381 + if [[ -n ${x} ]]; then
382 eerror "Trailing information in EGO_VENDOR in ${P}.ebuild"
383 eerror "${line}"
384 die "Invalid EGO_VENDOR format"
385 @@ -125,10 +347,10 @@ go-module_src_unpack() {
386 : "${repo:=${import}}"
387 vendor_tarballs+=("${repo//\//-}-${hash}.tar.gz")
388 done
389 - for f in $A; do
390 - [[ -n ${vendor_tarballs[*]} ]] && has "$f" "${vendor_tarballs[@]}" &&
391 + for f in ${A}; do
392 + [[ -n ${vendor_tarballs[*]} ]] && has "${f}" "${vendor_tarballs[@]}" &&
393 continue
394 - unpack "$f"
395 + unpack "${f}"
396 done
397
398 [[ -z ${vendor_tarballs[*]} ]] && return
399 @@ -143,6 +365,43 @@ go-module_src_unpack() {
400 -f "${DISTDIR}/${tarball}" || die
401 eend
402 done
403 + eqawarn "${P}.ebuild: EGO_VENDOR will be removed in the future."
404 + eqawarn "Please request that the author migrate to EGO_SUM."
405 +}
406 +
407 +# @FUNCTION: _go-module_src_unpack_verify_gosum
408 +# @DESCRIPTION:
409 +# Validate the Go modules declared by EGO_SUM are sufficient to cover building
410 +# the package, without actually building it yet.
411 +_go-module_src_unpack_verify_gosum() {
412 + # shellcheck disable=SC2120
413 + debug-print-function "${FUNCNAME}" "$@"
414 +
415 + if [[ ! ${_GO_MODULE_SET_GLOBALS_CALLED} ]]; then
416 + die "go-module_set_globals must be called in global scope"
417 + fi
418 +
419 + cd "${S}"
420 +
421 + # Cleanup the modules before starting anything else
422 + # This will print 'downloading' messages, but it's accessing content from
423 + # the $GOPROXY file:/// URL!
424 + einfo "Tidying go.mod/go.sum"
425 + go mod tidy >/dev/null
426 +
427 + # Verify that all needed modules are really present, by fetching everything
428 + # in the package's main go.mod. If the EGO_SUM was missing an entry then
429 + # 'go mod tidy' && 'go get' will flag it.
430 + # -v = verbose
431 + # -d = download only, don't install
432 + # -mod readonly = treat modules as readonly source
433 + einfo "Verifying linked Golang modules"
434 + go get \
435 + -v \
436 + -d \
437 + -mod readonly \
438 + all \
439 + || die "Some module is missing, update EGO_SUM"
440 }
441
442 # @FUNCTION: go-module_live_vendor
443 @@ -150,13 +409,14 @@ go-module_src_unpack() {
444 # This function is used in live ebuilds to vendor the dependencies when
445 # upstream doesn't vendor them.
446 go-module_live_vendor() {
447 - debug-print-function ${FUNCNAME} "$@"
448 + debug-print-function "${FUNCNAME}" "$@"
449
450 + # shellcheck disable=SC2086
451 has live ${PROPERTIES} ||
452 die "${FUNCNAME} only allowed in live ebuilds"
453 [[ "${EBUILD_PHASE}" == unpack ]] ||
454 die "${FUNCNAME} only allowed in src_unpack"
455 - [[ -d "${S}"/vendor ]] ||
456 + [[ -d "${S}"/vendor ]] &&
457 die "${FUNCNAME} only allowed when upstream isn't vendoring"
458
459 pushd "${S}" >& /dev/null || die
460 @@ -168,7 +428,7 @@ go-module_live_vendor() {
461 # @DESCRIPTION:
462 # Display a warning about security updates for Go programs.
463 go-module_pkg_postinst() {
464 - debug-print-function ${FUNCNAME} "$@"
465 + debug-print-function "${FUNCNAME}" "$@"
466 [[ -n ${REPLACING_VERSIONS} ]] && return 0
467 ewarn "${PN} is written in the Go programming language."
468 ewarn "Since this language is statically linked, security"
469 @@ -179,4 +439,30 @@ go-module_pkg_postinst() {
470 ewarn "stable tree."
471 }
472
473 +# @FUNCTION: _go-module_gomod_encode
474 +# @DESCRIPTION:
475 +# Encode the name(path) of a Golang module in the format expected by Goproxy.
476 +#
477 +# Upper letters are replaced by their lowercase version with a '!' prefix.
478 +#
479 +_go-module_gomod_encode() {
480 + ## Python:
481 + # return re.sub('([A-Z]{1})', r'!\1', s).lower()
482 +
483 + ## Sed:
484 + ## This uses GNU Sed extension \l to downcase the match
485 + #echo "${module}" |sed 's,[A-Z],!\l&,g'
486 + #
487 + # Bash variant:
488 + debug-print-function "${FUNCNAME}" "$@"
489 + #local re input lower
490 + re='(.*)([A-Z])(.*)'
491 + input="${1}"
492 + while [[ ${input} =~ ${re} ]]; do
493 + lower='!'"${BASH_REMATCH[2],}"
494 + input="${BASH_REMATCH[1]}${lower}${BASH_REMATCH[3]}"
495 + done
496 + echo "${input}"
497 +}
498 +
499 fi