mirror of
https://github.com/macports/mpbb.git
synced 2026-03-31 14:38:29 -07:00
247 lines
7.5 KiB
Bash
247 lines
7.5 KiB
Bash
#!/bin/bash
|
|
# -*- coding: utf-8; mode: sh; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=sh:et:sw=4:ts=4:sts=4
|
|
|
|
# Helper functions for mpbb
|
|
|
|
# Print $0 and arguments to standard error.
|
|
# Unset IFS to ensure "$*" uses spaces as separators.
|
|
msg() (unset IFS; printf >&2 '%s: %s\n' "$0" "$*")
|
|
err() { msg error: "$@"; }
|
|
warn() { msg warning: "$@"; }
|
|
|
|
unset GETOPT_COMPATIBLE
|
|
if getopt -T >/dev/null; then
|
|
# http://frodo.looijaard.name/project/getopt
|
|
err "Cannot find an enhanced getopt(1)"
|
|
return 3
|
|
fi
|
|
|
|
# TODO Documentation, obviously :)
|
|
parseopt() {
|
|
# Be stricter about this than getopt(1) is.
|
|
if ! [[ ${1-} =~ ^[[:alnum:]-]+:{0,2}(,[[:alnum:]-]+:{0,2})*$ ]]; then
|
|
err 'Invalid argument given to parseopt'
|
|
return 3
|
|
fi
|
|
|
|
# Use "--options +" to prevent arguments from being rearranged.
|
|
local opts
|
|
opts=$(getopt --name "$0" --opt + --longopt "$1" -- "${@:2}")
|
|
case $? in
|
|
0)
|
|
;;
|
|
1)
|
|
# getopt(1) will print the bad argument to standard error.
|
|
echo >&2 "Try \`$0 help' for more information."
|
|
return 2
|
|
;;
|
|
*)
|
|
err 'getopt encountered an internal error'
|
|
return 3
|
|
;;
|
|
esac
|
|
readonly opts
|
|
|
|
local -a validopts
|
|
IFS=, read -ra validopts <<<"$1"
|
|
readonly validopts=("${validopts[@]/#/--}")
|
|
|
|
eval set -- "$opts"
|
|
|
|
local opt validopt
|
|
# getopt(1) ensures that the options are always terminated with "--".
|
|
while [[ $1 != -- ]]; do
|
|
opt=$1
|
|
shift
|
|
# XXX Do NOT touch anything below unless you know exactly what
|
|
# you're doing (http://mywiki.wooledge.org/BashFAQ/006#eval).
|
|
for validopt in "${validopts[@]}"; do
|
|
if [[ $validopt == "$opt:" || $validopt == "$opt::" ]]; then
|
|
opt=${opt#--}
|
|
# $1 is null for omitted optional arguments.
|
|
eval option_"${opt//-/_}"'=$1'
|
|
shift
|
|
continue 2
|
|
fi
|
|
if [[ $validopt == "$opt" ]]; then
|
|
opt=${opt#--}
|
|
eval option_"${opt//-/_}"'=1'
|
|
continue 2
|
|
fi
|
|
done
|
|
# Unreachable unless there is a bug in this function or in getopt(1).
|
|
err 'parseopt encountered an internal error'
|
|
return 3
|
|
done
|
|
# shellcheck disable=SC2034
|
|
args=("${@:2}")
|
|
}
|
|
|
|
## Compute a failcache hash for the given port
|
|
#
|
|
# Computes and prints a hash uniquely identifying a specific state of a port's
|
|
# definition files, including the Portfile's hash as well as the port's
|
|
# patchfiles. To build the hash, this function executes the following
|
|
# algorithm:
|
|
# - For the Portfile, and each file in files/ (if any), calculate a SHA256
|
|
# hash
|
|
# - Sort the hash values alphabetically
|
|
# - Hash the result using SHA256
|
|
# This means a failcache entry will not match if a patchfile changes. A common
|
|
# case where this is the desired behavior is a port committed without
|
|
# a required patchfile.
|
|
#
|
|
# Valid arguments are all arguments accepted by "port dir".
|
|
compute_failcache_hash() {
|
|
local portdir
|
|
local -a filelist
|
|
|
|
portdir=$("${option_prefix}/bin/port" dir "$@")
|
|
if [ $? -ne 0 ] || [ -z "$portdir" ]; then
|
|
err "Could not compute failcache hash: port dir" "$@" "failed"
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -d "$portdir" ]; then
|
|
err "Port directory $portdir does not exist"
|
|
return 2
|
|
fi
|
|
|
|
filelist=("$portdir/Portfile")
|
|
if [ -d "$portdir/files" ]; then
|
|
filelist+=("$portdir/files")
|
|
fi
|
|
|
|
find "${filelist[@]}" -type f -exec openssl dgst -sha256 {} \; |\
|
|
cut -d' ' -f2 |\
|
|
sort |\
|
|
openssl dgst -sha256 |\
|
|
cut -d' ' -f2
|
|
}
|
|
|
|
## Compute a key that uniquely identifies a (port, variants, portfile-hash) tuple
|
|
#
|
|
# Valid arguments are a port name, optionally followed by a variant
|
|
# specification. Invokes "port dir" to find the Portfile and patchfiles and
|
|
# computes a checksum of these files that will become part of the hash.
|
|
failcache_key() {
|
|
local port=$1
|
|
if [ -z "$port" ]; then
|
|
err "failcache_key expects a port argument, but none was given."
|
|
return 1
|
|
fi
|
|
|
|
local checksum
|
|
checksum=$(compute_failcache_hash "$port")
|
|
if [ $? -ne 0 ]; then
|
|
err "compute_failcache_hash $port failed"
|
|
return 2
|
|
fi
|
|
|
|
local canonical_variants
|
|
canonical_variants=$("${option_prefix}/bin/port-tclsh" "${thisdir}/tools/canonical-variants.tcl" "$@")
|
|
if [ $? -ne 0 ]; then
|
|
err "tools/canonical-variants.tcl" "$@" "failed"
|
|
return 4
|
|
fi
|
|
|
|
echo "$port $canonical_variants $checksum"
|
|
}
|
|
|
|
## Delete stale failcache entries for a given port
|
|
#
|
|
# Valid arguments are the first and last parts of a key generated by
|
|
# failcache_key in order, i.e. portname and checksum.
|
|
# Returns 1 if there were no entries for the given port, 0 otherwise.
|
|
failcache_cleanup() {
|
|
if ! compgen -G "${option_failcache_dir}/${1} *" > /dev/null; then
|
|
return 1
|
|
fi
|
|
for f in "${option_failcache_dir}/${1} "*; do
|
|
if [ "$(basename "$f" | cut -d ' ' -f3)" != "${2}" ]; then
|
|
rm -f "${f}"
|
|
fi
|
|
done
|
|
return 0
|
|
}
|
|
|
|
## Test whether a given port with variants has previously failed.
|
|
#
|
|
# Valid arguments are a port name, optionally followed by a variant
|
|
# specification. Succeeds if the port did not previously fail to build,
|
|
# fails if the port is known to fail.
|
|
failcache_test() {
|
|
local key
|
|
key=$(failcache_key "$@")
|
|
if [ $? -ne 0 ]; then
|
|
err "Could not determine failcache key for" "$@"
|
|
return 1
|
|
fi
|
|
|
|
# check that it doesn't exceed NAME_MAX
|
|
if [ "$(echo "$key" | wc -c)" -gt 255 ]; then
|
|
printf "failcache key too long: %s\n" "${key}"
|
|
return 0
|
|
fi
|
|
|
|
if [ -f "${option_failcache_dir}/${key}" ]; then
|
|
printf "port %s previously failed in build %s\n" "${key}" "$(<"${option_failcache_dir}/${key}")"
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
## Mark a build of a given port with variants as successful.
|
|
#
|
|
# Valid arguments are a port name, optionally followed by a variant
|
|
# specification. Removes any database entries that marked a port as failed.
|
|
failcache_success() {
|
|
local key
|
|
key=$(failcache_key "$@")
|
|
if [ $? -ne 0 ]; then
|
|
err "Could not determine failcache key for" "$@"
|
|
return 1
|
|
fi
|
|
|
|
if [ "$(echo "$key" | wc -c)" -gt 255 ]; then
|
|
printf "failcache key too long: %s\n" "${key}"
|
|
return 0
|
|
fi
|
|
|
|
# Only remove the entry for the successful configuration, leaving
|
|
# other entries in case they are explicitly requested later. These
|
|
# can be removed manually if desired.
|
|
rm -f "${option_failcache_dir}/${key}"
|
|
}
|
|
|
|
## Mark a build of a given port with variants as failed.
|
|
#
|
|
# Valid arguments are a port name, optionally followed by a variant
|
|
# specification. Creates or updates the timestamp of a database entry that
|
|
# marks a port as failed.
|
|
failcache_failure() {
|
|
local key
|
|
key=$(failcache_key "$@")
|
|
if [ $? -ne 0 ]; then
|
|
err "Could not determine failcache key for" "$@"
|
|
return 1
|
|
fi
|
|
|
|
if [ "$(echo "$key" | wc -c)" -gt 255 ]; then
|
|
printf "failcache key too long: %s\n" "${key}"
|
|
return 0
|
|
fi
|
|
|
|
mkdir -p "${option_failcache_dir}"
|
|
echo "${BUILDBOT_BUILDURL:-unknown}" > "${option_failcache_dir}/${key}"
|
|
}
|
|
|
|
get-maintainers() {
|
|
# 'username@macports.org github_username' => 'username@macports.org'
|
|
# $option_prefix is set in mpbb
|
|
# shellcheck disable=SC2154
|
|
"${option_prefix}/bin/port" info --index --maintainers --line "$@" \
|
|
| tr ', ' '\n' | fgrep '@' | tr '\n' ',' | sed 's/,$//'
|
|
}
|