#!/bin/bash # Copyright © 2018 Nicolas Mailhot # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # SPDX-License-Identifier: GPL-3.0-or-later usage() { cat >&2 << EOF_USAGE Usage: $0 [ [-h] ] [ [-p ] [-g ] ] should be one of: provides, requires Most actions accept the same set of arguments, and will silently ignore those that do not apply to a specific action. Unless specified otherwise, all arguments are optional. Arguments: -h print this help -p : an optionnal prefix path such as %{buildroot} -g : the root of the Go source tree default value if not set: /usr/share/gocode EOF_USAGE exit 1 } action='' prefix='' gopath=/usr/share/gocode declare -A metadata flags_d=() flags_t=() flags_r=() flags_e=() if [[ $# -eq 0 ]] ; then usage else case $1 in provides|requires) action=$1 ;; *) usage ;; esac fi shift if ! options=$(getopt -n $0 -o hp:g:s: \ -l help,prefix:,go-path: \ -- "$@") then usage fi eval set -- "$options" while [ $# -gt 0 ] ; do case $1 in -h|--help) usage ;; -p|--prefix) prefix=$(realpath -sm "$2") ; shift;; -g|--go-path) gopath="$2" ; shift;; (--) shift; break;; (-*) usage ;; (*) break;; esac shift done dedupearray() { local -n arrayref="${1}" if [[ "${#arrayref[@]}" != "0" ]] ; then local temparray=( "${arrayref[@]}" ) arrayref=() while read -r -d $'\0' l ; do arrayref+=( "${l}" ) done < <(printf "%s\0" "${temparray[@]}" | sort -z -u) fi } readmetadata() { medadatafile="${1}" unset metadata declare -g -A metadata flags_d=() flags_t=() flags_r=() flags_e=() for m in version tag commit branch ; do v=$(grep "^${m}\:" "${medadatafile}" | head -1) v="${v#${m}:}" [[ -n "${v}" ]] && metadata["${m}"]="${v}" done while read -r -d $'\n' l ; do flags_d+=( "${l#excludedir:}" ) done < <(grep "^excludedir:" "${medadatafile}") dedupearray flags_d while read -r -d $'\n' l ; do flags_t+=( "${l#excludetree:}" ) done < <(grep "^excludetree:" "${medadatafile}") dedupearray flags_t while read -r -d $'\n' l ; do flags_r+=( "${l#excluderegex:}" ) done < <(grep "^excluderegex:" "${medadatafile}") dedupearray flags_r } expandflags() { echo ${flags_d[@]/#/ -d } ${flags_t[@]/#/ -t } ${flags_r[@]/#/ -r } ${flags_e[@]/#/ -e } } provides() { local goipath="${1}" ( echo "golang-symlink(${goipath})" GOPATH="${prefix}${gopath}" GO111MODULE=off \ golist --provided --package-path "${goipath}" $(expandflags) \ --skip-self --template 'golang({{.}})\n' ) | while IFS= read -r prov ; do echo "${prov}${metadata[version]:+ = ${metadata[version]}}" for m in "${!metadata[@]}" ; do if [[ "${m}" != "version" ]] ; then echo "${prov}(${m}=${metadata[${m}]})${metadata[version]:+ = ${metadata[version]}}" fi done done } requires() { local r="golang-ipath(${1#${prefix}${gopath}/src/})" echo "${r}${metadata[version]:+ = ${metadata[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 target gopath isgopathdir() { local lt="$1" if [[ -d ${lt} ]] && [[ "${lt}"/ == "${prefix}${gopath}"/src/* ]] ; then true else false fi } # Find the best .goipath match and load it findlockdir() { local lt="$1" while [[ "${lt}" != "${prefix}${gopath}/src" && ! -e "${lt}/.goipath" ]] ; do lt=$(dirname ${lt}) done [[ -e "${lt}/.goipath" ]] && echo "${lt}" || : } processlink() { local link="$1" local nexttarget=$(resolvelink "${link}") local linktarget=$(ultimateresolvelink "${nexttarget}") if isgopathdir "${linktarget}" ; then local lockdir=$(findlockdir "${linktarget}") [[ -e "${lockdir}/.goipath" ]] && readmetadata "${lockdir}/.goipath" case ${action} in provides) provides "${link#${prefix}${gopath}/src/}" ;; requires) echo "go-filesystem" requires "${lockdir}" ;; esac fi } # go.attr ensures that every time a package declares owning a symlink under # %{gopath}/src, symlink name will be piped to this script to compute the # package Go provides/requires. # # 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" fi done