nim / rpms / go-compilers

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