-
Notifications
You must be signed in to change notification settings - Fork 2
206 lines (185 loc) · 9.13 KB
/
Copy pathrelease.yml
File metadata and controls
206 lines (185 loc) · 9.13 KB
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
name: release
# Build prebuilt, stripped gratttool binaries for the common workshop targets
# and attach them to a GitHub Release so attendees can download-and-run without
# installing Rust or any build toolchain.
on:
push:
tags:
- 'v*' # e.g. v1.0.0
workflow_dispatch: # manual trigger for testing (builds + uploads CI
# artifacts, but only publishes a Release on a v* tag)
permissions:
contents: write # required to create/update the GitHub Release
jobs:
build:
name: Build ${{ matrix.artifact }}
# ubuntu-22.04 is the OLDEST generally-available GitHub-hosted runner
# (glibc 2.35); ubuntu-20.04 (glibc 2.31) was retired in 2025. The native
# binary is dynamically linked against the runner's glibc, so building on
# the oldest runner keeps it loadable on older student distros. A binary
# built against a newer glibc dies at startup with
# "version `GLIBC_2.xx' not found" on machines with an older libc.
# (The aarch64/armv7 binaries are built inside cross's own older-glibc
# images, so they get an even lower baseline regardless of this runner.)
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
# Primary: most laptops and VMs. Native build on the runner.
- artifact: gratttool-linux-x86_64
target: x86_64-unknown-linux-gnu
cross: false
strip: strip
# 64-bit Raspberry Pi OS, Apple Silicon Linux VMs.
- artifact: gratttool-linux-aarch64
target: aarch64-unknown-linux-gnu
cross: true
strip: aarch64-linux-gnu-strip
pkg_config_path: /usr/lib/aarch64-linux-gnu/pkgconfig
# 32-bit Raspberry Pi OS on ARMv7 boards (Pi 2/3/4, Zero 2 W runs
# 64-bit so use aarch64 there instead). arm_arch asserts the ELF is
# actually ARMv7 in the verify step below.
- artifact: gratttool-linux-armv7
target: armv7-unknown-linux-gnueabihf
cross: true
strip: arm-linux-gnueabihf-strip
pkg_config_path: /usr/lib/arm-linux-gnueabihf/pkgconfig
arm_arch: v7
# ARMv6 hard-float: the ONLY build that runs on the original ARMv6
# boards — Pi 1 and the original Pi Zero / Zero W. The armv7 build
# SIGILLs on these. The Rust target `arm-unknown-linux-gnueabihf`
# (note: bare `arm-`, not `armv7-`) emits ARMv6 + VFPv2 codegen by
# default; there are no RUSTFLAGS/target-cpu overrides in this repo
# that would force a newer ISA. armhf shares the arm-linux-gnueabihf
# multiarch ABI dir with armv7, so libdbus + strip are identical;
# only the codegen differs. arm_arch asserts ARMv6 in verify below.
#
# rustflags: Debian's libdbus-1.so has an internal NEEDED on
# libsystemd.so.0, which is NOT present in this target's crosstool-NG
# cross sysroot (and isn't pulled in as a dep there). gratttool never
# calls libsystemd directly — only libdbus does, internally — so this
# symbol is resolved at runtime on the Pi, where a full systemd
# userland always provides libsystemd.so.0. --allow-shlib-undefined
# tells ld not to error on undefined symbols that originate inside a
# shared library (libdbus); it still fully checks gratttool's own
# symbols. Avoids installing libsystemd + its whole transitive tree
# into the build container. Set ONLY on this target — armv7/aarch64
# use the Debian multiarch gcc, which resolves it at link time, so
# they stay untouched.
- artifact: gratttool-linux-armv6
target: arm-unknown-linux-gnueabihf
cross: true
strip: arm-linux-gnueabihf-strip
pkg_config_path: /usr/lib/arm-linux-gnueabihf/pkgconfig
arm_arch: v6
rustflags: "-C link-arg=-Wl,--allow-shlib-undefined"
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
# --- Native (x86_64): libdbus dev headers must be present so the
# libdbus-sys build script's pkg-config call resolves against the host.
- name: Install native build dependencies
if: ${{ !matrix.cross }}
run: |
sudo apt-get update
sudo apt-get install -y libdbus-1-dev pkg-config
# --- Cross: install the foreign-arch strip binaries on the runner so we
# can strip the aarch64/armv7/armv6 outputs after the build (the actual
# libdbus cross-link happens inside the cross container, see Cross.toml).
# armv6 and armv7 share the arm-linux-gnueabihf-strip binary.
- name: Install cross strip tools
if: ${{ matrix.cross }}
run: |
sudo apt-get update
sudo apt-get install -y binutils-aarch64-linux-gnu binutils-arm-linux-gnueabihf
- name: Install cross
if: ${{ matrix.cross }}
run: cargo install cross --locked
- name: Build (native)
if: ${{ !matrix.cross }}
run: cargo build --release --locked --target ${{ matrix.target }}
# PKG_CONFIG_* tell the in-container pkg-config to resolve the foreign
# libdbus: allow cross probing, point at the target arch's .pc dir, and
# keep the sysroot at the container root where the :arm64/:armhf package
# was installed. These names are passed through to the container by the
# [build.env] passthrough list in Cross.toml.
- name: Build (cross)
if: ${{ matrix.cross }}
env:
PKG_CONFIG_ALLOW_CROSS: "1"
PKG_CONFIG_SYSROOT_DIR: /
PKG_CONFIG_PATH: ${{ matrix.pkg_config_path }}
# Empty for targets that don't set matrix.rustflags (no-op); only the
# armv6 target sets it, to add -rpath-link for libdbus's transitive
# libsystemd. Passed into the cross container via Cross.toml passthrough.
RUSTFLAGS: ${{ matrix.rustflags }}
run: cross build --release --locked --target ${{ matrix.target }}
# Don't assume the cross link worked — prove libdbus actually got linked.
# A missing/misconfigured pkg-config would otherwise produce a binary
# that fails only at runtime on an attendee's Pi.
- name: Verify binary architecture and libdbus link
run: |
BIN="target/${{ matrix.target }}/release/gratttool"
file "$BIN"
if ! readelf -d "$BIN" | grep -qi 'libdbus-1'; then
echo "ERROR: libdbus-1 is not in the binary's dynamic dependencies."
echo "The cross pkg-config setup did not link libdbus — see Cross.toml."
readelf -d "$BIN" || true
exit 1
fi
echo "OK: libdbus-1 is linked."
# For the arm hard-float targets, assert the actual ISA baked into
# the ELF ARM attributes. armv6 and armv7 share the armhf ABI and the
# same libdbus/strip, so the ONLY thing that makes the armv6 build run
# on a Pi 1 / Zero is Tag_CPU_arch being v6. If a stray target-cpu/
# RUSTFLAG ever forces v7 here, this fails the build instead of
# shipping a binary that SIGILLs on ARMv6 hardware.
if [ -n "${{ matrix.arm_arch }}" ]; then
CPU_ARCH=$(readelf -A "$BIN" | sed -n 's/.*Tag_CPU_arch: //p' | head -n1)
echo "ELF Tag_CPU_arch: ${CPU_ARCH} (expected ${{ matrix.arm_arch }})"
if [ "$CPU_ARCH" != "${{ matrix.arm_arch }}" ]; then
echo "ERROR: ${{ matrix.target }} produced Tag_CPU_arch '$CPU_ARCH', expected '${{ matrix.arm_arch }}'."
echo "An ARMv6 target must NOT emit v7 codegen or it will SIGILL on Pi 1 / Zero."
readelf -A "$BIN" || true
exit 1
fi
echo "OK: ELF ISA is ${{ matrix.arm_arch }} as expected."
fi
- name: Strip binary
run: |
${{ matrix.strip }} "target/${{ matrix.target }}/release/gratttool"
cp "target/${{ matrix.target }}/release/gratttool" "${{ matrix.artifact }}"
ls -lh "${{ matrix.artifact }}"
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: ${{ matrix.artifact }}
if-no-files-found: error
release:
name: Publish GitHub Release
needs: build
runs-on: ubuntu-22.04
# Only publish a Release for an actual tag push; a manual workflow_dispatch
# run still builds + uploads CI artifacts above for testing, but skips this.
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
steps:
- name: Download all build artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true # flatten every artifact into dist/
- name: List artifacts
run: ls -lh dist
- name: Publish release
uses: softprops/action-gh-release@v2
with:
files: dist/*
generate_release_notes: true # auto-generate notes from commits/PRs
fail_on_unmatched_files: true