modules: the protocol wrappers cross the boundary, via the purview #16
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Build every member on BOTH toolchains, and check what a green compile would | |
| # not: that the generator is ours, that the sonames are canonical, that the two | |
| # libraries do not overlap, and that every module produced an interface. | |
| name: ci | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ['v*'] | |
| pull_request: | |
| workflow_dispatch: | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # Both, because the module wrappers are where the two compilers differ | |
| # most: a 300-entry `using ::name;` export block is exactly the shape | |
| # that finds a disagreement about what may be named in an export. | |
| toolchain: [default, llvm] | |
| name: build (${{ matrix.toolchain }}) | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Installed the way a user does, through xlings, rather than by | |
| # unpacking an mcpp release: these packages depend on the ECOSYSTEM | |
| # (`xim:mesa`'s GBM discovery row, the toolchains), and a pinned mcpp | |
| # tarball carries a frozen snapshot of it. Testing against the current | |
| # ecosystem is the point. | |
| # | |
| # XLINGS_NON_INTERACTIVE is what makes the installer usable here. Without | |
| # it the script takes its `curl | bash` branch — `xlings self install < | |
| # /dev/tty` — and a runner has a /dev/tty that is readable but not | |
| # connected, so it dies with "No such device or address" before anything | |
| # is installed. | |
| - name: Install xlings + mcpp | |
| env: | |
| XLINGS_NON_INTERACTIVE: "1" | |
| run: | | |
| curl -fsSL https://d2learn.org/xlings-install.sh | bash | |
| echo "$HOME/.xlings/bin" >> "$GITHUB_PATH" | |
| "$HOME/.xlings/bin/xlings" --version | |
| "$HOME/.xlings/bin/xlings" install mcpp | |
| echo "$HOME/.xlings/subos/default/bin" >> "$GITHUB_PATH" | |
| - name: Select toolchain | |
| if: matrix.toolchain != 'default' | |
| run: | | |
| mcpp toolchain install ${{ matrix.toolchain }} | |
| mcpp toolchain default ${{ matrix.toolchain }} | |
| mcpp toolchain list | |
| - name: Build the workspace | |
| run: | | |
| mcpp --version | |
| mcpp build --workspace | |
| - name: The macro mappings actually run | |
| run: mcpp test -p mcpp/util | |
| # Artifacts are located by searching the tree rather than by assuming | |
| # `mcpp/<member>/target/...`: where a workspace build puts its output is | |
| # mcpp's business and has moved before. On a miss the step prints what it | |
| # DID find, so the next failure is diagnosable from the log alone. | |
| - name: wayland-scanner is ours, and is 1.26.0 | |
| run: | | |
| scanner=$(find . -name wayland-scanner -type f -perm -u+x | head -1) | |
| if [ -z "$scanner" ]; then | |
| echo "::error::no wayland-scanner binary anywhere in the tree" | |
| find . -path ./upstream -prune -o -name 'target' -type d -print | |
| exit 1 | |
| fi | |
| echo "found: $scanner" | |
| "$scanner" --version | |
| "$scanner" --version | grep -qx 'wayland-scanner 1.26.0' | |
| - name: the generated protocol code came from THIS scanner | |
| run: | | |
| scanner=$(find . -name wayland-scanner -type f -perm -u+x | head -1) | |
| "$scanner" -s public-code upstream/protocol/wayland.xml /tmp/wayland-protocol.c | |
| head -1 /tmp/wayland-protocol.c | |
| grep -q 'Generated by wayland-scanner 1.26.0' /tmp/wayland-protocol.c | |
| - name: both libraries carry the canonical SONAMEs | |
| run: | | |
| for pair in "client:libwayland-client.so.0" "server:libwayland-server.so.0"; do | |
| member=${pair%%:*}; want=${pair#*:} | |
| so=$(find . -name "${want%.0}" -type f | head -1) | |
| test -n "$so" || { echo "$member: no library built"; exit 1; } | |
| got=$(readelf -d "$so" | sed -n 's/.*SONAME.*\[\(.*\)\]/\1/p') | |
| echo "$member -> $got" | |
| test "$got" = "$want" || { echo "expected $want"; exit 1; } | |
| done | |
| - name: the two libraries do not overlap | |
| run: | | |
| # Mesa's libEGL_mesa has DT_NEEDED on both; if one carried the other's | |
| # API the process would bind to whichever loaded first. | |
| c=$(find . -name libwayland-client.so -type f | head -1) | |
| s=$(find . -name libwayland-server.so -type f | head -1) | |
| readelf --dyn-syms -W "$c" | grep -q ' wl_display_connect' || exit 1 | |
| readelf --dyn-syms -W "$c" | grep -q ' wl_display_create' && { echo "client leaked the server API"; exit 1; } | |
| readelf --dyn-syms -W "$s" | grep -q ' wl_display_create' || exit 1 | |
| readelf --dyn-syms -W "$s" | grep -q ' wl_display_connect' && { echo "server leaked the client API"; exit 1; } | |
| echo "client and server APIs are disjoint" | |
| - name: mcpp/generated/ matches what the scanner produces | |
| run: | | |
| # The generated protocol code is checked in because it is the | |
| # package's PUBLIC interface (wayland-client.h includes it), so a | |
| # build-time include dir cannot carry it. Checked in means it can | |
| # drift; this is what stops it. | |
| scanner=$(find . -name wayland-scanner -type f -perm -u+x | head -1) | |
| X=upstream/protocol/wayland.xml | |
| mkdir -p /tmp/regen | |
| "$scanner" -s public-code "$X" /tmp/regen/wayland-protocol.c | |
| for side in client server; do | |
| "$scanner" -s $side-header "$X" /tmp/regen/wayland-$side-protocol.h | |
| "$scanner" -s $side-header -c "$X" /tmp/regen/wayland-$side-protocol-core.h | |
| done | |
| for f in /tmp/regen/*; do | |
| n=$(basename "$f") | |
| if ! diff -q "$f" "mcpp/generated/$n" >/dev/null; then | |
| echo "::error file=mcpp/generated/$n::out of date — regenerate it (see mcpp/generated/README.md)" | |
| diff "$f" "mcpp/generated/$n" | head -20 | |
| exit 1 | |
| fi | |
| done | |
| echo "generated code matches wayland.xml and this scanner" | |
| - name: the module wrappers match the generator | |
| run: | | |
| # The .cppm files and the -module.h copies are generated from the | |
| # public headers. Checked in, so they can drift; this stops it. | |
| python3 mcpp/tools/genmod.py . | |
| if ! git diff --quiet; then | |
| echo "::error::module wrappers are out of date — run mcpp/tools/genmod.py ." | |
| git diff --stat | |
| exit 1 | |
| fi | |
| echo "module wrappers match" | |
| - name: every module produced an interface | |
| run: | | |
| for m in wayland.client wayland.server wayland.util; do | |
| find . \( -name "$m.gcm" -o -name "$m.pcm" \) | head -1 | grep -q . \ | |
| || { echo "::error::$m has no module interface"; find . -name '*.gcm' -o -name '*.pcm'; exit 1; } | |
| echo "$m ok" | |
| done | |
| echo "wayland.client, wayland.server and wayland.util all have interfaces" | |
| upstream: | |
| # `upstream/` must be the release tarball, byte for byte. | |
| # | |
| # Checked by COMPARING, not by building: this repository builds one way, | |
| # through mcpp, and keeping a second build system alive just to assert | |
| # something would be a second thing to maintain and a second thing to | |
| # disagree with the first. Comparing is also the stronger check — meson | |
| # succeeding proves the tree still builds, not that nothing was edited. | |
| name: upstream/ is the release tarball, unmodified | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: diff upstream/ against wayland 1.26.0 | |
| run: | | |
| curl -L -fsS -o wayland.tar.xz \ | |
| "https://gitlab.freedesktop.org/wayland/wayland/-/releases/1.26.0/downloads/wayland-1.26.0.tar.xz" | |
| echo "64176eaa46e4969903e286f8e5ef8331affc17fdf03ac9b58381d2b23162b7a3 wayland.tar.xz" | sha256sum -c - | |
| mkdir -p /tmp/pristine | |
| tar -xJf wayland.tar.xz -C /tmp/pristine | |
| # .gitignore lives at the repo root here, not inside upstream/ — it is | |
| # this repository's, covering mcpp's build output. It is the one | |
| # expected difference. | |
| if diff -r --exclude=.gitignore /tmp/pristine/wayland-1.26.0 upstream; then | |
| echo "upstream/ is pristine" | |
| else | |
| echo "::error::upstream/ differs from the wayland 1.26.0 release tarball." | |
| echo "Everything this fork adds belongs under mcpp/." | |
| exit 1 | |
| fi |