summaryrefslogtreecommitdiff
path: root/tests/symbols_test.sh
blob: 0343eb02a9a2231aeab1fed3053e888c052d816a (plain)
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
#!/usr/bin/env bash
# Every global symbol of the term library starts with term_. A host links
# the library next to its own code, so a bare name such as palette_init can
# collide with a host symbol. Every function that term.h declares is also
# defined, so the library cannot leave one out. A shared build exports
# exactly the functions of term.h.
set -euo pipefail
lib=$1
src=${2:-$(dirname "$0")/../include/term}
nm=${NM:-nm}

defined_args=(-g --defined-only -P)
exported_args=(-D --defined-only -P)
if [[ $(uname -s) == Darwin ]]; then
    defined_args=(-g -U -P)
    exported_args=(-g -U -P)
fi
syms=$("$nm" "${defined_args[@]}" "$lib")
# ASan adds an __odr_asan.<name> symbol for each global variable.
defined=$(awk 'NF >= 2 && $2 ~ /^[A-Z]$/ && $2 != "U" { print $1 }' <<<"$syms" |
          grep -v '^_*odr_asan\.' | sed 's/^_//' | sort -u)

status=0
bad=$(grep -v '^term_' <<<"$defined" || true)
if [[ -n $bad ]]; then
    echo "$lib: global symbols without the term_ prefix:" >&2
    echo "$bad" >&2
    status=1
fi

declared=$("${CC:-cc}" -std=c11 -E -P -I"$src" "$src/term.h" | python3 -c '
import re, sys
text = re.sub(r"\s+", " ", sys.stdin.read())
depth, flat = 0, []
for ch in text:
    depth += ch == "{"
    if depth == 0:
        flat.append(ch)
    depth -= ch == "}"
    if ch == "}" and depth == 0:
        flat.append(";")
for stmt in "".join(flat).split(";"):
    if "static" in stmt or "typedef" in stmt:
        continue
    m = re.search(r"\b(term_\w+)\s*\(", stmt)
    if m:
        print(m.group(1))
' | sort -u)
missing=$(comm -23 <(echo "$declared") <(echo "$defined"))
if [[ -n $missing ]]; then
    echo "$lib: functions declared in term.h but not defined:" >&2
    echo "$missing" >&2
    status=1
fi

# The shared library exports term.h and nothing else.
shared=${3:-}
if [[ -n $shared ]]; then
    exported=$("$nm" "${exported_args[@]}" "$shared" | awk '$2 ~ /^[TDBRVW]$/ { print $1 }' |
               sed 's/^_//' | sort -u)
    extra=$(comm -13 <(echo "$declared") <(echo "$exported"))
    absent=$(comm -23 <(echo "$declared") <(echo "$exported"))
    if [[ -n $extra || -n $absent ]]; then
        echo "$shared: exports differ from term.h" >&2
        [[ -n $extra ]] && echo "not in term.h: $extra" >&2
        [[ -n $absent ]] && echo "not exported: $absent" >&2
        status=1
    fi
fi
exit $status