Blame rpm/gosymlink.deps

Packit fec9a0
#!/bin/bash
Packit fec9a0
# Copyright (c) 2018 Nicolas Mailhot <nim@fedoraproject.org>
Packit fec9a0
# This file is distributed under the terms of GNU GPL license version 3, or
Packit fec9a0
# any later version.
Packit fec9a0
Packit fec9a0
usage() {
Packit fec9a0
cat >&2 << EOF_USAGE
Packit fec9a0
Usage: $0 <action> [ [-h] ]
Packit fec9a0
                   [ [-p <prefix>] [-g <go path>] ]
Packit fec9a0
                   [ [-v <version>] ] [ [-a <attribute>] ]
Packit fec9a0
Packit fec9a0
<action>             should be one of: provides, requires
Packit fec9a0
Packit fec9a0
Most actions accept the same set of arguments, and will silently ignore those
Packit fec9a0
that do not apply to a specific action. Unless specified otherwise, all
Packit fec9a0
arguments are optional.
Packit fec9a0
Packit fec9a0
“provides”-specific arguments:
Packit fec9a0
Packit fec9a0
-v <version string>: tag the provides with <version string>
Packit fec9a0
-a <attribute>:      an attribute to add to the provides, for example
Packit fec9a0
                     -a "(commit=XXXX)"
Packit fec9a0
                     -a "(branch=YYYY)"
Packit fec9a0
                     -a "(tag=rx.y.z-alpha1)"
Packit fec9a0
                     can be specified several times
Packit fec9a0
Packit fec9a0
“requires”-specific arguments:
Packit fec9a0
Packit fec9a0
-v <version string>: tag symbolink link target requires with <version string>
Packit fec9a0
Packit fec9a0
Common arguments:
Packit fec9a0
Packit fec9a0
-h                   print this help
Packit fec9a0
-p <prefix>:         an optionnal prefix path such as %{buildroot}
Packit fec9a0
-g <go path>:        the root of the Go source tree
Packit fec9a0
                     default value if not set: /usr/share/gocode
Packit fec9a0
EOF_USAGE
Packit fec9a0
exit 1
Packit fec9a0
}
Packit fec9a0
Packit fec9a0
action=''
Packit fec9a0
version=''
Packit fec9a0
prefix=''
Packit fec9a0
gopath=/usr/share/gocode
Packit fec9a0
declare -A attributes
Packit fec9a0
Packit fec9a0
if [[ $# -eq 0 ]] ; then
Packit fec9a0
  usage
Packit fec9a0
else case $1 in
Packit fec9a0
    provides|requires) action=$1 ;;
Packit fec9a0
    *)                 usage ;;
Packit fec9a0
  esac
Packit fec9a0
fi
Packit fec9a0
Packit fec9a0
shift
Packit fec9a0
Packit fec9a0
if ! options=$(getopt -n $0 -o hp:gv:a: \
Packit fec9a0
                      -l help,prefix:,go-path: \
Packit fec9a0
                      -l version:,attribute: \
Packit fec9a0
                      -- "$@")
Packit fec9a0
then
Packit fec9a0
    usage
Packit fec9a0
fi
Packit fec9a0
Packit fec9a0
eval set -- "$options"
Packit fec9a0
Packit fec9a0
while [ $# -gt 0 ] ; do
Packit fec9a0
  case $1 in
Packit fec9a0
    -h|--help)                      usage ;;
Packit fec9a0
    -p|--prefix)                    prefix=$(realpath -sm "$2")  ; shift;;
Packit fec9a0
    -g|--go-path)                   gopath="$2"                  ; shift;;
Packit fec9a0
    -v|--version)                   version="$2"                 ; shift;;
Packit fec9a0
    -a|--attribute)                 IFS=')' read -r -a newattrs <<< "$2"
Packit fec9a0
                                      for index in "${!newattrs[@]}" ; do
Packit fec9a0
                                        newattrs[index]=${newattrs[index]#\(}
Packit fec9a0
                                        attributes[${newattrs[index]%%=*}]=${newattrs[index]#*=}
Packit fec9a0
                                    done                         ; shift;;
Packit fec9a0
    (--)          shift; break;;
Packit fec9a0
    (-*)          usage ;;
Packit fec9a0
    (*)           break;;
Packit fec9a0
  esac
Packit fec9a0
  shift
Packit fec9a0
done
Packit fec9a0
Packit fec9a0
deco=( "" )
Packit fec9a0
for key in "${!attributes[@]}"; do
Packit fec9a0
  [ -n "${attributes[$key]}" ] && deco+=( "($key=${attributes[$key]})" )
Packit fec9a0
done
Packit fec9a0
Packit fec9a0
Packit fec9a0
# Convert paths within gopath to version-constrained provides
Packit fec9a0
provides() {
Packit fec9a0
 local package="${1#${prefix}${gopath}/src/}"
Packit fec9a0
 for index in "${!deco[@]}" ; do
Packit fec9a0
   echo "golang(${package})${deco[index]}${version:+ = ${version}}"
Packit fec9a0
 done
Packit fec9a0
}
Packit fec9a0
Packit fec9a0
# Convert paths within gopath to version-constrained requires
Packit fec9a0
requires() {
Packit fec9a0
  local package="${1#${prefix}${gopath}/src/}"
Packit fec9a0
  echo "golang(${package})${version:+ = ${version}}"
Packit fec9a0
}
Packit fec9a0
Packit fec9a0
# Resolve a symlink target in presence of a build root
Packit fec9a0
resolvelink() {
Packit fec9a0
  local lt=$(realpath -m "$1")
Packit fec9a0
  echo "${prefix}${lt#${prefix}}"
Packit fec9a0
}
Packit fec9a0
Packit fec9a0
# Resolve a symlink to its ultimate target in presence of a build root
Packit fec9a0
ultimateresolvelink() {
Packit fec9a0
  local lt="$1"
Packit fec9a0
  until [[ ! -L ${lt} ]] ; do
Packit fec9a0
    lt=$(resolvelink "${lt}")
Packit fec9a0
  done
Packit fec9a0
  echo "${lt}"
Packit fec9a0
}
Packit fec9a0
Packit fec9a0
# Test if a path is a directory within the target gopath
Packit fec9a0
isgopathdir() {
Packit fec9a0
  local lt="$1"
Packit fec9a0
  if [[ -d ${lt} ]] && [[ "${lt}"/ == "${prefix}${gopath}"/src/* ]] ; then
Packit fec9a0
    true
Packit fec9a0
  else
Packit fec9a0
    false
Packit fec9a0
  fi
Packit fec9a0
}
Packit fec9a0
Packit fec9a0
# A symlink can point to a whole directory tree, but go.attr will only
Packit fec9a0
# trigger on the root symlink.
Packit fec9a0
# Therefore, check the symlink points within the processed import path, then
Packit fec9a0
# walk all the target tree to generate symlink provides/requires
Packit fec9a0
#
Packit fec9a0
# To process nested symlinks the function needs to be provided a working path
Packit fec9a0
# to the symlink tip within the build root as second argument.
Packit fec9a0
processlink() {
Packit fec9a0
  local link="$1"
Packit fec9a0
  local nexttarget=$(resolvelink "$2")
Packit fec9a0
  local linktarget=$(ultimateresolvelink "${nexttarget}")
Packit fec9a0
  if isgopathdir "${linktarget}" ; then
Packit fec9a0
    case ${action} in
Packit fec9a0
      provides) find "${linktarget}" -type d -print | while read subdir ; do
Packit fec9a0
                  provides    "${link}${subdir#${linktarget}}"
Packit fec9a0
                done
Packit fec9a0
                find "${linktarget}" -type l -print | while read sublink ; do
Packit fec9a0
                  processlink "${link}${sublink#${linktarget}}" "${sublink}"
Packit fec9a0
                done ;;
Packit fec9a0
      requires) # Requires is not recursive — it relies on providers requiring their own needs
Packit fec9a0
                find "${nexttarget}" -type d -print | while read subdir ; do
Packit fec9a0
                  requires   "${subdir}"
Packit fec9a0
                done
Packit fec9a0
                find "${nexttarget}" -type l -print | while read sublink ; do
Packit fec9a0
                  if isgopathdir $(ultimateresolvelink "${sublink}") ; then
Packit fec9a0
                  # The owner of the link will declare the correct requires
Packit fec9a0
                    requires "${sublink}"
Packit fec9a0
                  fi
Packit fec9a0
                done ;;
Packit fec9a0
    esac
Packit fec9a0
  fi
Packit fec9a0
}
Packit fec9a0
Packit fec9a0
# go.attr ensures that every time a package declares owning a symlink under
Packit fec9a0
# %{gopath}/src, symlink name will be piped to this script to compute the
Packit fec9a0
# package Go provides/requires.
Packit fec9a0
#
Packit fec9a0
# For legacy reason the script is supposed to be able to handle multiple
Packit fec9a0
# inputs, even though modern rpm invokes it separately for each directory.
Packit fec9a0
while read dir ; do
Packit fec9a0
  if [[ -L $dir ]] ; then
Packit fec9a0
    processlink "$dir" "$dir"
Packit fec9a0
  fi
Packit fec9a0
done | sort -u | grep -v '/\([._]\)\?\(internal\|testdata\|vendor\)\([/)]\)'