-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
functions
246 lines (218 loc) · 7.55 KB
/
functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#!/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=$("$(readlink "${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() {
# '[email protected] github_username' => '[email protected]'
# $option_prefix is set in mpbb
# shellcheck disable=SC2154
"${option_prefix}/bin/port" info --index --maintainers --line "$@" \
| tr ', ' '\n' | fgrep '@' | tr '\n' ',' | sed 's/,$//'
}