Development workflow notes for libjpeg-turbo-rs. User-facing
documentation lives in README.md and on
docs.rs.
- All changes go through pull requests; CI must be green (the workspace gate cross-validates against C libjpeg-turbo byte-for-byte).
cargo fmt --allandcargo clippy --lib -- -D warningsbefore each commit (git config core.hooksPath .githooksinstalls the pre-commit hook).- Tests follow TDD and must cross-validate against C
djpeg/cjpeg/jpegtranwhere a C contract exists — seeCLAUDE.mdfor the full testing rules, anddocs/LAST_MILE.mdfor the live release gate.
Both integration oracle jobs run every C-ABI test target, with PNG support:
LIBJPEG_TURBO_PREFIX=/path/to/v8/install \
LIBJPEG_TURBO_REFERENCE_DIR=/path/to/v8/install \
cargo test -p libjpeg-turbo-rs-capi --tests --features png --no-fail-fastCargo discovers new test targets automatically. oracle_version_pins guards
this complete selection on both jobs; a named subset or compile-only command
cannot replace it. The explicit v8 prefix makes the shared classic/TurboJPEG
oracle helpers mandatory. Some older downstream harnesses still have their
own prerequisite skips; executing every target does not prove every external
consumer was available. --no-fail-fast collects failures across test binaries.
scripts/issue_loop.sh can work through the open issues without supervision,
one fresh agent per issue; CLAUDE.md documents how to run it. Two label
conventions come out of that, and both are meant for humans to use:
autofix-skip— put it on an umbrella or tracker issue that closes only when its children do, so no agent tries to "fix" the tracker itself.autofix-blocked— the loop applies this after two attempts produce no merged pull request, and an agent applies it itself when the issue needs a human decision or hardware it does not have. Either way it means read the issue comments; removing the label puts the issue back in the queue.
Run reports land in target/issue-loop-logs/ on the machine that ran the loop.
Requires a nightly toolchain (rustup install nightly) and the rust-src component:
rustup component add rust-src --toolchain nightlyAddressSanitizer (detects heap overflows, use-after-free, stack overflows):
RUSTFLAGS="-Z sanitizer=address" \
LSAN_OPTIONS="suppressions=$(pwd)/lsan_suppressions.txt:detect_leaks=1" \
cargo +nightly test --workspace --lib \
--target x86_64-unknown-linux-gnu \
--no-fail-fast -- --test-threads=1UB checks (detects signed integer overflow, invalid enum discriminant, misaligned pointer dereference):
RUSTFLAGS="-Z ub-checks=yes" \
cargo +nightly test --workspace --lib \
--no-fail-fast -- --test-threads=1Note: rustc does not implement sanitizer=undefined; -Z ub-checks=yes is the correct nightly knob for runtime UB detection.
All three sanitizer jobs (asan, ubsan, and the P4-11 C-boundary asan harness) run on every PR via .github/workflows/sanitizers.yml. macOS is excluded because the NEON SIMD paths produce spurious cross-thread ASan shadow-map false positives under parallel test execution.
crates/libjpeg-turbo-rs-capi/examples/cabi_misuse_harness.c is a C driver that
dlopens one shared library, runs one named case, and exits — so a case that
faults is one child's exit status rather than the whole run's. It needs no
special toolchain:
cc -O1 -g -Wall -Wextra -o /tmp/cabi_misuse_harness crates/libjpeg-turbo-rs-capi/examples/cabi_misuse_harness.c -ldl -lpthread
cargo build -p libjpeg-turbo-rs-capi
/tmp/cabi_misuse_harness target/debug/liblibjpeg_turbo_rs_capi.so pitch_boundaries references/libjpeg-turbo/testimages/testorig.jpgPoint it at <prefix>/lib/libturbojpeg.so instead and you get the same
transcript from stock TurboJPEG; that comparison is what
crates/libjpeg-turbo-rs-capi/tests/cabi_misuse_harness.rs automates, and it
requires an oracle at or above the tool-current release in
docs/oracle_versions.tsv (it checks for tj3InitVersion). Lines the two
implementations are known to disagree on live in that file's
KNOWN_DIVERGENCES (and, for the sixteen tj3Set pairs one item owns, in
APPLICABILITY_DIVERGENCES), each naming the LAST_MILE item that owns it and
each required to still diverge, so a fix deletes its entry.
The same cases run under ASan and UBSan in sanitizers.yml's c_boundary_asan
job.