#!/bin/bash usage() { cat >&2 << EOF_USAGE Usage: $0 -g [-p ] [-G ] [-v ] [-a ] the Go import path of the target package : an eventual prefix path such as %{buildroot} : the root of the Go source tree : tag the provides with the : attributes to add to the provides, for example (commit=XXXX)(branch=YYYY) or (vermod=alpha1) Example: echo filename | $0 -g golang.org/x/net -G /usr/share/gocode -v 2.5-0.1 echo filename | $0 -g golang.org/x/net -a '(commit=XXXX)(branch=YYYY)' echo filename | $0 -g golang.org/x/net -a '(vermod=alpha1)' EOF_USAGE exit 1 } if ! options=$(getopt -n $0 -o hg:p:G:v:a: -l help,goipath:,prefix:,gopath:,version:,attr:,attribute:,attributes: -- "$@") then # something went wrong, getopt will put out an error message for us exit 2 fi eval set -- "$options" # Convert paths within gopath to version-constrained provides # Never call it before checking the path belongs to the package import path provides() { local ppath="$1" # Check for a vendor root, when not processing a symlink if ! ( [[ -L "${ppath}" ]] || ( notvendored "${ppath}" ) ) ; then local vroot="$(realpath -sm "${ppath}")/" vroot="${vroot%/vendor/*}/vendor" if [[ "${vroot}" != "${ppath}" ]] ; then # Check if a parent with files exists within the vendor root local tip="${ppath}" local vipath=/dev/null until [[ "${tip}" == "${vroot}" || "${ppath}" == "${vipath}/"* ]] ; do [[ -n $(find "$tip" -maxdepth 1 -type f) ]] && vipath="$tip" tip=$(dirname "$tip") done # If we didn't find a parent with files other than the path itself, it is # the vendored import path root! if [[ "$vipath" == "$ppath" ]] ; then echo "bundled(golang(${ppath##*/vendor/}))" fi fi else local package="${ppath#${root}/src/}" for index in "${!deco[@]}" ; do echo "golang(${package})${deco[index]}${version}" done fi } # Resolve a symlink target in presence of a build root resolvelink() { local lt=$(realpath -m "$1") echo "${prefix}${lt#${prefix}}" } # Resolve a symlink to its ultimate target in presence of a build root ultimateresolvelink() { local lt="$1" until [[ ! -L ${lt} ]] ; do lt=$(resolvelink "${lt}") done echo "${lt}" } # Test if a path is a directory within the package import path. isgoipathdir() { local lt="$1" if [[ -d ${lt} ]] && [[ "${lt}"/ == "${root}/src/${goipath}"/* ]] ; then true else false fi } # Test if a path is vendored notvendored() { local p=$(realpath -sm "$1") local vr="${p}/" vr="${vr%/vendor/*}" if [[ "${vr}" != "${p}/" ]] ; then false else true fi } # A symlink can point to a whole directory tree, but go.attr will only # trigger on the root symlink. # Therefore, check the symlink points within the processed import path, then # walk all the target tree to generate symlink provides # # To process nested symlinks the function needs to be provided a working path # to the symlink tip within the build root as second argument. processlink() { local link="$1" local linktarget=$(ultimateresolvelink "$2") if ( isgoipathdir "${linktarget}" ) && ( notvendored "${linktarget}" ) ; then find "${linktarget}" -type d -print | while read subdir ; do provides "${link}${subdir#${linktarget}}" done find "${linktarget}" -type l -print | while read sublink ; do processlink "${link}${sublink#${linktarget}}" "${sublink}" done fi } version='' prefix='' gopath=/usr/share/gocode declare -A attributes while [ $# -gt 0 ] ; do case $1 in -h|--help) usage ;; -g|--goipath) goipath="$2" ; shift;; -p|--prefix) prefix=$(realpath -sm "$2") ; shift;; -G|--gopath) gopath="$2" ; shift;; -v|--version) version=" = $2" ; shift;; -a|--attr|--attribute|--attributes) IFS=')' read -r -a newattrs <<< "$2" for index in "${!newattrs[@]}" ; do newattrs[index]=${newattrs[index]#\(} attributes[${newattrs[index]%%=*}]=${newattrs[index]#*=} done ; shift;; (--) shift; break;; (-*) echo "$0: error - unrecognized option $1" >&2; exit 3;; (*) break;; esac shift done # Detection of the package import path root is necessary to insure we do not # generate useless provides such as golang(github). # # When go dep stabilizes and can be generalized import path root detection may # be automated by searching for the first level of go dep lock files. # # However the requirement to set %{goipath} may remain to make sure we avoid # processing directories created without applying the sanity checks documented # in Fedora Packaging Guidelines. [[ -z ${goipath} ]] && exit 0 root="${prefix}${gopath}" deco=( "" ) # Some filtering may be needed in the future for key in "${!attributes[@]}"; do [ -n "${attributes[$key]}" ] && deco+=( "($key=${attributes[$key]})" ) done # go.attr ensures that every time a package declares owning a directory or # symlink under %{gopath}/src, the directory or symlink name will be piped to # this script to compute the package Go provides. # # For legacy reason the script is supposed to be able to handle multiple # inputs, even though modern rpm invokes it separately for each directory. # # As native Go tooling is focused on leaf developer needs (“what I need from # others to build my code”, not “what I provide to help others build their # code”) Go auto-provides do not invoke any Go binary and just record all the # directories created under the import path root. This is actually faster and # more reliable, since Go project seem to import pretty much any import path # subdirectory, even if it contains assets without any .go file. while read dir ; do if [[ -L $dir ]] ; then processlink "$dir" "$dir" else # If go.attr triggered on a normal directory just make sure it is under the # package import path, then provide it if [[ -d $dir ]] ; then dir=$(realpath -sm "$dir") [[ "${dir}"/ == "${root}/src/${goipath}"/* ]] && provides "$dir" fi fi done | sort | uniq | grep -v '/\([._]\)\?\(internal\|vendor\)\([/)]\)'