#!/bin/bash usage() { cat >&2 << EOF_USAGE Usage: $0 -g [-p ] [-G ] [-v ] the Go import path of the target package : an eventual prefix path such as %{buildroot} : the root of the Go source tree : tag symlinked renamings with Example: echo filename | $0 -g golang.org/x/net -G /usr/share/gocode EOF_USAGE exit 1 } if ! options=$(getopt -n $0 -o hg:p:G:v: -l help,goipath:,prefix:,gopath:,version: -- "$@") 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 requires # Never call it before checking the path belongs to the package import path requires() { local package="${1#${root}/src/}" echo "golang(${package})${version}" } # 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 requires # # Requires is not recursive — it relies on providers requiring their own needs processlink() { local link="$1" local nexttarget=$(resolvelink "$1") local linktarget=$(ultimateresolvelink "${nexttarget}") if ( isgoipathdir "${linktarget}" ) && ( notvendored "${linktarget}" ) ; then find "${nexttarget}" -type d -print | while read subdir ; do requires "${subdir}" done find "${nexttarget}" -type l -print | while read sublink ; do if isgoipathdir $(ultimateresolvelink "${sublink}") ; then # The owner of the link will declare the correct requires requires "${sublink}" fi done fi } prefix='' gopath=/usr/share/gocode 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;; (--) shift; break;; (-*) echo "$0: error - unrecognized option $1" >&2; exit 3;; (*) break;; esac shift done # Detection of the package import path root is necessary when processing # symlinks. We can only be sure the requires of a symlink target are correctly # computed, if it is processed here as part of the package import path tree. # # 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}" # 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 requires. # # When go dep stabilizes and can be generalized it will be possible to simplify # requires computation by running go dep on the import path root only. go dep # will also make possible to compute requires version constrains. However, # there will still be a need to trigger require computation on every symlink. # # For legacy reason the script is supposed to be able to handle multiple # inputs, even though modern rpm invokes it separately for each directory. while read dir ; do if [[ -L $dir ]] ; then processlink "$dir" else if [[ -d $dir ]] ; then # When triggered on a directory, make sure it is within the import path, # and not vendored, run go list to get the directory import, then filter # out standard Go imports provided by golang itself. # Invoking go is awfully slow. You *will* fear rpm has hung the first # time that happens. # Hopefully go dep will replace this with something faster soonish. dir=$(realpath -sm "$dir") if notvendored "$dir" ; then package="${dir#${root}/src/}" if $(ls ${dir}/*.go >/dev/null 2>&1) ; then GOPATH=$root go list -e -f \ '{{ join .Imports "\n" }}' "$package" |\ while read import ; do GOPATH=$root go list -e -f \ '{{if not .Standard}}{{.ImportPath | printf "golang(%s)\n"}}{{end}}' "$import" done fi fi fi fi done | sort | uniq \ | grep -v '\(/\([._]\)\?\(internal\|testdata\|vendor\)\([/)]\)\|golang(C)\)'