Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 212 additions & 30 deletions .github/cross-toolchain/cross-toolchain.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#!/usr/bin/env bash
# Which of 0113's target triples this runner can compile C for (#291).
# Which triples this runner can compile C for, over the target register's set
# and over what a record took out of it (#291, #514).
#
# THE SECOND POPULATION IS WHY #514 EXISTS. 0512 cut the target register to the
# triples this runner can build the graph 0243's entry brings, and a survey tied
# to that register alone then reports a set the runner serves by construction.
# The triples that left carry a `left:` field naming the record that took them
# out, so the survey keeps them and the day the image gains a compiler for one
# is a line in this leg's output rather than a thing somebody has to look for.
#
# WHAT THIS IS FOR. 0243 decides that a certificate is validated through
# `rustls`. Every crypto provider `rustls` offers is C. The `targets` leg beside
Expand Down Expand Up @@ -35,11 +43,13 @@
# of the one that passage carries, and the next such leg would make a fresh stale
# one, so the number is deleted rather than corrected.
#
# WHAT IT DOES REFUSE is a survey that cannot be read. A block missing a field,
# a block naming a field this loader does not know, a triple in the target register's set that
# no block covers, a block naming a triple that set does not carry, a derived
# count that disagrees with the blocks it came from, and a register naming
# nothing. Each of those exits zero and prints a page indistinguishable from a
# WHAT IT DOES REFUSE is a survey that cannot be read. A block missing a
# required field, a block naming a field this loader does not know, a triple in
# the target register's set that no block covers, a block naming a triple that
# set does not carry and declaring no record that took it out, a block declaring
# a departure for a triple that register still carries, a departure naming a
# record `docs/decisions/` does not hold, a derived count that disagrees with
# the blocks it came from, and a register naming nothing. Each of those exits zero and prints a page indistinguishable from a
# run that examined every triple in the target register's set and found them all served, which
# is the one reading this leg must never produce.
#
Expand Down Expand Up @@ -168,11 +178,90 @@ unknown_fields() {
if (sep == 0) { print line; next }
name = substr(line, 1, sep - 1)
gsub(/^[ \t]+|[ \t]+$/, "", name)
if (name != "triple" && name != "probe" && name != "reason") print name
if (name != "triple" && name != "probe" && name != "reason" && name != "left") print name
}
'
}

# The triple and the record of every block declaring that its triple left the
# target register's set, tab separated.
#
# `left` is the one optional field. A block without it is a probe for a triple
# the target register carries; a block with it is a probe for a triple that
# register used to carry, kept so that the day the image gains a compiler for a
# departed platform is a line in this leg's output rather than something
# somebody has to think of looking for.
#
# A block is read as a departure only where it also carries the three required
# fields, so an unfinished block is reported by the rule that reports unfinished
# blocks rather than counted here as well.
declared_departures() {
awk '
function flush() {
if (fields > 0 && triple != "" && probe != "" && reason != "" && left != "") {
printf "%s\t%s\n", triple, left
}
triple = ""; probe = ""; reason = ""; left = ""; fields = 0
}
{
line = $0
sub(/\r$/, "", line)
gsub(/^[ \t]+|[ \t]+$/, "", line)
if (line == "" || substr(line, 1, 1) == "#") { flush(); next }

sep = index(line, ":")
if (sep == 0) { fields = fields + 1; next }
name = substr(line, 1, sep - 1)
value = substr(line, sep + 1)
gsub(/^[ \t]+|[ \t]+$/, "", name)
gsub(/^[ \t]+|[ \t]+$/, "", value)
fields = fields + 1
if (name == "triple") triple = value
else if (name == "probe") probe = value
else if (name == "reason") reason = value
else if (name == "left") left = value
}
END { flush() }
'
}

# Where the two registers disagree, one line per disagreement, as the triple
# followed by the word for what is wrong with it.
#
# $1 is the target register's set, $2 the triples the probe register names, $3
# the triples it declares as departed. All three are newline separated.
#
# `unsurveyed` a triple the target register carries that no block covers. The
# leg would then say nothing about a platform on a page that reads
# as though it said something about all of them.
# `unexplained` a block naming a triple the target register does not carry and
# declaring no record that took it out. That is how a register
# outlives the platform it was written for, and the `left` field
# is what tells a departure from an oversight.
# `returned` a block declaring that its triple left the set while the target
# register carries it. One of the two registers was not updated,
# and which one is not a thing this leg can decide.
agreement_failures() {
local wanted="$1" probed="$2" departed="$3" triple
while IFS= read -r triple; do
[ -n "$triple" ] || continue
printf '%s\n' "$probed" | grep -qx -- "$triple" || printf '%s\tunsurveyed\n' "$triple"
done <<AGREEMENT_WANTED
$wanted
AGREEMENT_WANTED
while IFS= read -r triple; do
[ -n "$triple" ] || continue
if printf '%s\n' "$wanted" | grep -qx -- "$triple"; then
printf '%s\n' "$departed" | grep -qx -- "$triple" && printf '%s\treturned\n' "$triple"
else
printf '%s\n' "$departed" | grep -qx -- "$triple" || printf '%s\tunexplained\n' "$triple"
fi
done <<AGREEMENT_PROBED
$probed
AGREEMENT_PROBED
return 0
}

# How many blocks the register offers at all, read so that a loader which
# stopped matching cannot report an empty set and pass.
raw_blocks() {
Expand Down Expand Up @@ -214,6 +303,8 @@ judge_declared() { printf '%s' "$1" | declared_probes; }
judge_missing() { printf '%s' "$1" | blocks_missing_a_field; }
judge_unknown() { printf '%s' "$1" | unknown_fields; }
judge_raw() { printf '%s' "$1" | raw_blocks; }
judge_left() { printf '%s' "$1" | declared_departures; }
judge_agree() { agreement_failures "$1" "$2" "$3"; }

selftest() {
echo "== a block the survey runs =="
Expand Down Expand Up @@ -332,6 +423,53 @@ reason: The host.
"$(judge_unknown '# reasons: a comment is not a field line
')"

echo "== the field that says a triple left the target register's set =="
assert_out "reads: a block declaring the record that took its triple out" \
"$(printf 'aarch64-apple-ios\t0512')" \
"$(judge_left 'triple: aarch64-apple-ios
probe: clang --target=arm64-apple-ios
reason: The phone.
left: 0512
')"
assert_out "reads: nothing out of a block that declares no departure, which is nearly all of them" "" "$(judge_left 'triple: x86_64-unknown-linux-gnu
probe: cc
reason: The host.
')"
assert_out "does not read as a departure: a block whose left is whitespace" "" "$(judge_left "$(printf 'triple: aarch64-apple-ios
probe: clang
reason: The phone.
left:
')")"
assert_out "does not read as a departure: an unfinished block, which the rule above reports instead" "" "$(judge_left 'triple: aarch64-apple-ios
probe: clang --target=arm64-apple-ios
left: 0512
')"
assert_out "passes over: left named in a comment, which is where the register argues about the field" "" "$(judge_left '# left: 0512 is prose here and not a field.
')"
assert_out "reads as a field this loader knows: left, so a departure is not reported as a misspelling" "" "$(judge_unknown 'triple: aarch64-apple-ios
probe: clang
reason: The phone.
left: 0512
')"

echo "== the rule that refuses two registers which disagree =="
assert_out "passes: a triple in the target set with a probe, and a departed triple with one" "" "$(judge_agree 'x86_64-unknown-linux-gnu' 'x86_64-unknown-linux-gnu
aarch64-apple-ios' 'aarch64-apple-ios')"
assert_out "refuses: a triple the target register carries that no block covers" \
"$(printf 'armv7-linux-androideabi\tunsurveyed')" \
"$(judge_agree 'x86_64-unknown-linux-gnu
armv7-linux-androideabi' 'x86_64-unknown-linux-gnu' '')"
assert_out "refuses: a block outside the target set declaring no record that took the triple out" \
"$(printf 'aarch64-apple-tvos\tunexplained')" \
"$(judge_agree 'x86_64-unknown-linux-gnu' 'x86_64-unknown-linux-gnu
aarch64-apple-tvos' '')"
assert_out "refuses: a block saying its triple left while the target register carries it" \
"$(printf 'aarch64-apple-ios\treturned')" \
"$(judge_agree 'aarch64-apple-ios' 'aarch64-apple-ios' 'aarch64-apple-ios')"
assert_out "refuses: both directions at once, since a reader wants the whole list" \
"$(printf 'armv7-linux-androideabi\tunsurveyed\naarch64-apple-tvos\tunexplained')" \
"$(judge_agree 'armv7-linux-androideabi' 'aarch64-apple-tvos' '')"

echo "== the count that catches a loader which stopped matching =="
assert_out "counts: every run of field lines the register offers" \
"2" \
Expand Down Expand Up @@ -464,36 +602,62 @@ MISSING
return 1
fi

echo "-- the set this run surveys, read through the reader the targets leg uses"
departures="$(declared_departures < "$REGISTER" | sort -u)"
departed="$(printf '%s\n' "$departures" | cut -f1 | grep . | sort -u || true)"
probed="$(printf '%s\n' "$declared" | cut -f1 | sort -u)"

echo "-- the set this run surveys"
echo " the triples the target register names, read through the reader the targets leg uses"
echo " bash ${TARGETS} triples"
printf '%s\n' "$wanted" | sed 's/^/ /'
echo " the triples a record took out of that register, read from the left: field beside each probe"
if [ -n "$departed" ]; then
while IFS="$TAB" read -r triple record; do
[ -n "$triple" ] || continue
echo " ${triple}, which left under ${record}"
done <<DEPARTURES
$departures
DEPARTURES
else
echo " none"
fi
echo

while IFS= read -r triple; do
while IFS="$TAB" read -r triple record; do
[ -n "$triple" ] || continue
if ! printf '%s\n' "$declared" | cut -f1 | grep -qx -- "$triple"; then
echo "::error::${triple} is in the target register's set and ${REGISTER} carries no probe for it. A triple added to that set and left unsurveyed is a platform this run says nothing about, on a page that reads as though it said something about all of them."
if [ -z "$(find docs/decisions -name "${record}-*.md" 2> /dev/null)" ]; then
echo "::error::${REGISTER} says ${triple} left the set under ${record}, and docs/decisions holds no record of that number. A departure whose record cannot be opened is a platform nobody can find the argument for, which is the state this field exists against."
failures=$((failures + 1))
fi
done <<WANTED
$wanted
WANTED
done <<RESOLVE
$departures
RESOLVE

while IFS= read -r triple; do
while IFS="$TAB" read -r triple what; do
[ -n "$triple" ] || continue
if ! printf '%s\n' "$wanted" | grep -qx -- "$triple"; then
echo "::error::${REGISTER} carries a probe for ${triple} and the target register's set does not name that triple. A probe for a platform nobody links is a compiler this run reports on for no reason, and it is how a register outlives the platform it was written for."
failures=$((failures + 1))
fi
done <<DECLARED
$(printf '%s\n' "$declared" | cut -f1 | sort -u)
DECLARED
case "$what" in
unsurveyed)
echo "::error::${triple} is in the target register's set and ${REGISTER} carries no probe for it. A triple added to that set and left unsurveyed is a platform this run says nothing about, on a page that reads as though it said something about all of them."
;;
unexplained)
echo "::error::${REGISTER} carries a probe for ${triple}, the target register's set does not name that triple, and no block says which record took it out. A probe for a platform nobody links and nobody recorded leaving is a compiler this run reports on for no reason, and it is how a register outlives the platform it was written for. A departure says so with left: NNNN."
;;
returned)
echo "::error::${REGISTER} says ${triple} left the target register's set while that register carries it. One of the two was not updated when the triple came back, and which one is not a thing this leg can decide."
;;
esac
failures=$((failures + 1))
done <<DISAGREEMENTS
$(agreement_failures "$wanted" "$probed" "$departed")
DISAGREEMENTS

if [ "$failures" -ne 0 ]; then
echo "::error::the probe register and the target register's set disagree in ${failures} place(s). Nothing was compiled."
return 1
fi

surveyed="$(printf '%s\n%s\n' "$wanted" "$departed" | grep . | sort -u)"

workspace="$(mktemp -d)"
# shellcheck disable=SC2064
trap "rm -rf '${workspace}'" EXIT
Expand All @@ -506,11 +670,19 @@ DECLARED

echo "-- what each probe answered"
unserved=""
returnable=""
while IFS= read -r triple; do
[ -n "$triple" ] || continue
total=$((total + 1))
this_one_is_served=0
echo "-- ${triple}"
if printf '%s
' "$departed" | grep -qx -- "$triple"; then
in_the_set=0
echo "-- ${triple} (left the target register's set)"
else
in_the_set=1
echo "-- ${triple} (in the target register's set)"
fi
while IFS="$TAB" read -r name probe; do
[ "$name" = "$triple" ] || continue
[ -n "$probe" ] || continue
Expand All @@ -535,21 +707,29 @@ $declared
PROBES
if [ "$this_one_is_served" -eq 1 ]; then
served=$((served + 1))
echo " => ${triple}: a C compiler on this image compiles for it."
if [ "$in_the_set" -eq 1 ]; then
echo " => ${triple}: a C compiler on this image compiles for it."
else
returnable="${returnable}${triple} "
echo " => ${triple}: a C compiler on this image compiles for it, and it is not in the target register's set."
fi
else
unserved="${unserved}${triple} "
echo " => ${triple}: no probe on this image compiled for it."
fi
echo
done <<WANTED
$wanted
WANTED
done <<SURVEYED
$surveyed
SURVEYED

say "This runner compiles C for ${served} of the ${total} triple(s) the target register's set names."
say "This runner compiles C for ${served} of the ${total} triple(s) surveyed, which are the triples the target register names and the triples a record took out of it."
if [ "$served" -eq "$total" ]; then
say "EVERY TRIPLE IS SERVED. That is the reading 0291 names as one of the conditions that reverse it: the cross-toolchain way out then costs no runner bill and no maintained matrix, the reason for waiting is gone, and the choice between the three is retaken rather than inherited."
else
say "NOT SERVED: ${unserved% }. 0291's wait stands on this reading rather than in spite of it, and the manifest entry 0243 requires would redden the targets leg for each of those."
say "NOT SERVED: ${unserved% }. 0291's wait stands on this reading rather than in spite of it, and the manifest entry 0243 requires would redden the targets leg for each of those still in the set."
fi
if [ -n "$returnable" ]; then
say "SERVED AND OUT OF THE SET: ${returnable% }. Each of those left the target register under the record the survey named above, and this image now compiles C for it, which is 0512's reversal condition for that triple arriving. It is a reading and not a decision: what a probe here proves is one translation unit, and the triple returns by a build of the graph on the runner and a record that puts the line back."
fi

echo
Expand All @@ -559,8 +739,10 @@ WANTED
echo "NOT MEASURED HERE: a compiler nobody wrote a block for. The register holds what somebody named, so an image carrying a working cross-toolchain under a name that is not in it is reported unserved, and the repair is a block rather than a change to this script."
echo "NOT MEASURED HERE: any image but the one this job ran on. The verdict is this runner's at this moment, and an image that gains a toolchain next month changes it without anything here noticing."
echo "NOT MEASURED HERE: whether the wait should end. 0291 asks for two things and this is one of them; the other is a pure-Rust provider reaching a release the targets leg compiles on every triple, which is a version string and not an image."
echo "NOT MEASURED HERE: whether a triple that left should come back. A departed triple reported served here has met one half of 0512's reversal condition for it, on one translation unit; the other half is a build of the real graph for that triple on this runner, which is the targets leg and not this one, and the return itself is a record."
echo "NOT MEASURED HERE: a triple nobody put in either register. The survey is the target register plus the departures declared beside the probes, so a platform taken out by a record and left out of this file is surveyed by nothing and refused by nothing."
echo
echo "This leg refuses nothing about what it found. What it refuses is a survey that cannot be read, and every one of those rules ran its fixture above."
echo "This leg refuses nothing about what it found. What it refuses is a survey that cannot be read, and every one of those rules ran its fixture above - including the three ways the two registers can disagree, which are judged by one function so that each has a fixture rather than only a message."
}

case "${1:-}" in
Expand Down
Loading
Loading