Skip to content

release: the openipc.org push succeeds on a 200 and nothing else #120

release: the openipc.org push succeeds on a 200 and nothing else

release: the openipc.org push succeeds on a 200 and nothing else #120

Workflow file for this run

name: PR Build Check
on:
pull_request:
branches: [master]
jobs:
build:
name: Build ${{ matrix.target }} (musl static)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- target: arm32
toolchain_url: https://github.com/OpenIPC/firmware/releases/download/toolchain/toolchain.hisilicon-hi3516cv100.tgz
toolchain_dir: arm-openipc-linux-musleabi_sdk-buildroot
cc: arm-openipc-linux-musleabi-gcc
- target: mips32
toolchain_url: https://github.com/OpenIPC/firmware/releases/download/toolchain/toolchain.ingenic-t31.tgz
toolchain_dir: mipsel-openipc-linux-musl_sdk-buildroot
cc: mipsel-openipc-linux-musl-gcc
- target: arm64
# Bootlin's prebuilt aarch64 musl cross toolchain. Switch
# to an OpenIPC-hosted tarball once one is published.
toolchain_url: https://toolchains.bootlin.com/downloads/releases/toolchains/aarch64/tarballs/aarch64--musl--stable-2025.08-1.tar.xz
toolchain_dir: aarch64--musl--stable-2025.08-1
cc: aarch64-buildroot-linux-musl-gcc
steps:
- uses: actions/checkout@v5
with:
# The size step below builds the PR's base commit as well as its
# head, so the history has to be there to check out.
fetch-depth: 0
- name: Fetch toolchain
run: |
# Download to file (with retries) before extracting — piping
# wget -> tar makes transient stream truncations look like
# corrupt archives; the wget retry only helps with discrete
# files.
wget --tries=3 --timeout=60 -O /tmp/toolchain.tar "${{ matrix.toolchain_url }}"
# `tar xf` autodetects gzip/bzip2/xz so the matrix entries
# can mix archive formats freely.
tar xf /tmp/toolchain.tar -C /opt
rm /tmp/toolchain.tar
- name: Build
run: |
export PATH=/opt/${{ matrix.toolchain_dir }}/bin:$PATH
cmake -H. -Bbuild -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_BUILD_TYPE=Release
cmake --build build
- name: ipcinfo size
# ipcinfo ships on all but three of the OpenIPC firmware tree's board
# configs, onto cameras whose rootfs cap leaves tens of KB spare, and
# nothing here measured it until now -- the job above builds a static
# binary, which is not what those images carry.
#
# The configure below reproduces the firmware package's: shared libs
# (which is what drops the global -static), SKIP_VERSION, and
# CMAKE_C_FLAGS_RELEASE cleared. That last one matters. CMake appends
# its own "-O3 -DNDEBUG" after CMAKE_C_FLAGS, so a plain Release
# configure silently measures -O3; Buildroot's toolchain file clears
# the variable, which is why the firmware genuinely gets the -Os this
# file asks for.
#
# The comparison is against the PR's base commit, not a number written
# down here. This job's toolchain is not one any camera is built with
# -- it is whatever toolchain.hisilicon-hi3516cv100.tgz ships, and it
# produces an ipcinfo some 12 KB larger than the gcc 13.3 toolchains
# the boards use -- so an absolute ceiling here would describe no
# shipped binary and would go stale the moment that URL moves. A
# delta does not care which compiler measured it.
if: matrix.target == 'arm32'
run: |
export PATH=/opt/${{ matrix.toolchain_dir }}/bin:$PATH
measure() {
out=$(mktemp -d)
cmake -H"$1" -B"$out" -DCMAKE_C_COMPILER=${{ matrix.cc }} \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS_RELEASE=" -DNDEBUG" \
-DBUILD_SHARED_LIBS=ON -DSKIP_VERSION=ON \
-DIPCHW_VENDORS="$2" > /dev/null
cmake --build "$out" --target ipcinfo > /dev/null
stat -c %s "$out/ipcinfo"
}
git worktree add -f /tmp/ipcinfo-base \
"${{ github.event.pull_request.base.sha }}" > /dev/null
status=0
# "all" is what the firmware tree builds today; "none" is what it
# builds once its ipctool.mk passes the knob added in #178.
for vendors in all none; do
base=$(measure /tmp/ipcinfo-base "$vendors")
head=$(measure . "$vendors")
delta=$((head - base))
echo "ipcinfo, IPCHW_VENDORS=$vendors: $base -> $head ($delta bytes)"
if [ "$delta" -gt 1024 ]; then
echo "::error::ipcinfo (IPCHW_VENDORS=$vendors) grows $delta bytes. Over the 1024-byte budget: say in the PR why the camera should carry it."
status=1
elif [ "$delta" -gt 0 ]; then
echo "::warning::ipcinfo (IPCHW_VENDORS=$vendors) grows $delta bytes."
fi
done
exit $status
test-extraction-pipeline:
name: Test sensor extraction pipeline
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Run end-to-end pipeline smoke test
run: tools/test_pipeline.sh
- name: Check the HiSilicon sensor-clock suite is still wired in
# In this job and not in unit-tests, for the reason that job's own
# guard gives: a check that runs only once the thing it protects has
# run protects nothing. Anchored to a bare invocation line so this
# step's own text cannot satisfy it.
run: |
grep -qE '^[[:space:]]*\./build/hal_hisi_test[[:space:]]*$' \
.github/workflows/pr-build-check.yml \
|| { echo "no job in this workflow runs hal_hisi_test"; exit 1; }
unit-tests:
name: Unit tests (native)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Check the pipeline suite is still wired in
# Deliberately NOT inside test_pipeline.sh: a check that only runs
# once the thing it protects has already run protects nothing.
# It lives in this job rather than test-extraction-pipeline for the
# same reason -- both jobs are required, so dropping that job's step
# leaves this one still failing.
# Matches a run: line and nothing else. A bare grep for the script
# name finds this very step's own text and can never fail.
run: |
grep -qE '^[[:space:]]*run:[[:space:]]*tools/test_pipeline\.sh[[:space:]]*$' \
.github/workflows/pr-build-check.yml \
|| { echo "no job in this workflow runs tools/test_pipeline.sh"; exit 1; }
- name: Build and run
run: |
cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release
cmake --build build --target cYAML_test reginfo_test longse_test \
sensors_test anyka_test hal_hisi_test \
report_test
./build/cYAML_test
./build/report_test
./build/reginfo_test
./build/longse_test
./build/anyka_test
./build/hal_hisi_test
# Run from the tree root: the fingerprint it compares against is
# src/sensors_golden.txt, and a sensor that stopped being identified
# has to fail here rather than be noticed on a camera in a ceiling.
./build/sensors_test src/sensors_golden.txt
- name: Build trimmed, and run again
# The pad tables are the largest thing in libipchw and a consumer
# picks the families it targets. Prove the trimmed configure
# compiles and that reginfo_test still passes when most of the
# tables are not there.
run: |
cmake -H. -Bbuild-trimmed -DCMAKE_BUILD_TYPE=Release \
-DIPCHW_VENDORS=none -DIPCHW_PADMUX=v1 -DIPCHW_HISI=v4
cmake --build build-trimmed --target reginfo_test
./build-trimmed/reginfo_test
- name: Every IPCHW_HISI selection compiles
# Each generation guards a different set of arms -- a chip-ID case, a
# temperature formula, a sensor-bus back-end, a die-ID reader -- and a
# guard that swallows a #define or leaves a dangling else only shows up
# in the one selection that needs it. Three of these broke while the
# knob was being written, each in a different generation, so sweep all
# of them rather than trusting the two that a person happens to build.
# libipchw alone, natively: it is the target that carries the guards,
# and it keeps the sweep to a few seconds.
run: |
status=0
for gen in all none v1 v2 v2a "v2;v2a" v3 v3a v4 v4a v5 3536c 3536d; do
dir=build-hisi-$(echo "$gen" | tr -d ';')
if cmake -H. -B"$dir" -DCMAKE_BUILD_TYPE=Release \
-DIPCHW_VENDORS=none -DIPCHW_HISI="$gen" > "$dir.log" 2>&1 \
&& cmake --build "$dir" --target ipchw >> "$dir.log" 2>&1; then
echo "IPCHW_HISI=$gen ok"
else
echo "::error::IPCHW_HISI=$gen does not build"
tail -30 "$dir.log"
status=1
fi
done
exit $status
- name: The interned pad names are current
# src/padmux_names.{h,c} are generated from the MUXCTRL tables, and
# nothing at build time regenerates them: a cross build must not need
# python3 in the toolchain container. So the staleness check is here.
# A row that names a function the table does not carry fails to
# compile, which is the loud half; a name left in the blob after its
# last row went is the quiet half, and this is what catches it.
run: python3 tools/gen_padmux_names.py --check
- name: Every IPCHW_PADMUX selection compiles, and its tables still read
# Same shape as the IPCHW_HISI sweep above and the same reason, only
# sharper since the names were interned: each family's entries in
# struct padmux_names sit behind that family's #if, so a guard that
# names the wrong family builds everywhere except the one selection
# that needs it. reginfo_test is built as well as ipchw because a
# table that compiles and reads back the wrong name is the failure
# this is really for.
run: |
status=0
for fam in all none v1 v2 v2a v3 v3a v4 v4a v5 3536c 3536d \
sstar ingenic "v4;sstar"; do
dir=build-padmux-$(echo "$fam" | tr -d ';')
if cmake -H. -B"$dir" -DCMAKE_BUILD_TYPE=Release \
-DIPCHW_PADMUX="$fam" > "$dir.log" 2>&1 \
&& cmake --build "$dir" --target reginfo_test >> "$dir.log" 2>&1 \
&& ./"$dir"/reginfo_test >> "$dir.log" 2>&1; then
echo "IPCHW_PADMUX=$fam ok"
else
echo "::error::IPCHW_PADMUX=$fam does not build or does not pass"
tail -30 "$dir.log"
status=1
fi
done
exit $status
- name: IPCHW_HISI's default follows IPCHW_VENDORS
# A default is the kind of thing that drifts without anyone noticing,
# and the two spellings that must NOT move are the two ipcinfo is
# built as: its own "all", and the firmware package's "none", which
# means only the always-in vendors and so IS a HiSilicon build. A
# silently HiSi-blind ipcinfo would be worse than a large one.
#
# Configure only -- this asserts the cache value, not a binary.
run: |
status=0
check() { # <vendors|-> <hisi|-> <expected>
d=$(mktemp -d)
args=(-H. -B"$d" -DSKIP_VERSION=ON -DONLY_LIBRARY=ON)
[ "$1" != "-" ] && args+=(-DIPCHW_VENDORS="$1")
[ "$2" != "-" ] && args+=(-DIPCHW_HISI="$2")
if ! cmake "${args[@]}" > "$d/log" 2>&1; then
echo "::error::configure failed for VENDORS=$1 HISI=$2"
tail -20 "$d/log"; status=1; rm -rf "$d"; return
fi
got=$(grep '^IPCHW_HISI:STRING=' "$d/CMakeCache.txt" | cut -d= -f2-)
if [ "$got" = "$3" ]; then
printf 'ok VENDORS=%-16s HISI=%-8s -> %s\n' "$1" "$2" "$got"
else
echo "::error::VENDORS=$1 HISI=$2 gave IPCHW_HISI=$got, wanted $3"
status=1
fi
rm -rf "$d"
}
check - - all # ipctool's own default
check all - all # ipcinfo's default: must not move
check none - all # firmware's ipcinfo: a HiSi build
check ingenic - none # a narrowing implies no HiSilicon
check sstar - none
check "ingenic;sstar" - none
check ingenic all all # an explicit value always wins
check ingenic v4 v4
check all none none
# And the same question asked of ONE build directory, reconfigured.
# This is where the first cut of this was wrong: the derived value
# lands in the cache, and on the next configure it is
# indistinguishable from one the user typed -- so a tree first
# configured with a narrowed vendor kept IPCHW_HISI=none when it was
# later broadened to `all`, and that build could not identify a
# HiSilicon camera. Build directories are reused across boards.
d=$(mktemp -d)
step() { # <cmake args...> <expected>
exp="${!#}"; set -- "${@:1:$#-1}"
if ! cmake -H. -B"$d" -DSKIP_VERSION=ON -DONLY_LIBRARY=ON "$@" \
> "$d/log" 2>&1; then
echo "::error::reconfigure failed: $*"; tail -20 "$d/log"
status=1; return
fi
got=$(grep '^IPCHW_HISI:' "$d/CMakeCache.txt" | cut -d= -f2-)
if [ "$got" = "$exp" ]; then
printf 'ok reconfigure %-38s -> %s\n' "$*" "$got"
else
echo "::error::reconfigure $* gave IPCHW_HISI=$got, wanted $exp"
status=1
fi
}
step -DIPCHW_VENDORS=ingenic none # narrowed
step -DIPCHW_VENDORS=all all # broadened: must follow
step -DIPCHW_VENDORS=sstar none # narrowed again
step -DIPCHW_VENDORS=none all # a HiSilicon build
rm -rf "$d"; d=$(mktemp -d)
step -DIPCHW_VENDORS=all -DIPCHW_HISI=v4 v4 # explicit
step -DIPCHW_VENDORS=ingenic v4 # and it survives a vendor change
rm -rf "$d"; d=$(mktemp -d)
step -DIPCHW_VENDORS=ingenic none
step -DIPCHW_HISI=all all # explicit on a LATER configure
step -DIPCHW_VENDORS=sstar all # still theirs
rm -rf "$d"
exit $status