Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# The vendored C/C++ oracles (libaom, dav1d, libavif, libheif, exiv2, libjpeg-turbo, the DNG SDK,
# libjxl, …) dominate CI: they are workspace members under `tooling/`, and Swatinem/rust-cache
# deliberately drops workspace crates — including their build-script OUT_DIRs, where every static
# archive lives — so before sccache each heavy job rebuilt all fourteen from scratch (~9 min, paid
# twice per PR).
# libjxl, …) dominate CI: they are local path crates — the `tooling/` oracles are
# `[workspace].exclude` entries reached as path dev-dependencies, and libjxl builds inside the
# member `gamut-jxl-sys` — and Swatinem/rust-cache deliberately drops local crates, including
# their build-script OUT_DIRs where every static archive lives, so before sccache each heavy job
# rebuilt all fourteen from scratch (~9 min, paid twice per PR).
#
# sccache is the right tool because it is content-addressed on preprocessed source + compiler +
# flags: a `third_party/` submodule bump changes the content, changes the hash, and misses. There
Expand Down
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ chokepoint. So a native build failure is **not** explained by the invoking shell
settings — do not "fix" it by overriding `CC`/`CXX` per command. `GAMUT_BUILD_KEEP_ENV=1`
opts out (and confirms a suspected env interaction is real).

A build-script failure that is **not** a compile error — cargo reporting that it could not run
the build script itself — is usually a lost owner-execute bit under `target/debug/build/`, not a
code problem. The damaged mode is `-rw-rwx---`: the group keeps `x`, so `ls` output looks
unremarkable, but cargo runs as the owner and the owner class is what the kernel checks. Recover
with `chmod -R u+x target/debug/build/` and re-run. The cause is environmental and outside this
repo — nothing here sets file modes — so do not work around it by editing build scripts.

## Conventions

- All `pub` items need doc comments. Mark fallible/owning return types `#[must_use]` where
Expand Down
30 changes: 30 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,41 @@ run = "rustup component add rustfmt --toolchain nightly 2>/dev/null || rustup to
description = "Format code (nightly rustfmt; merge-resilient imports)"
depends = ["ensure-nightly-rustfmt"]
run = "cargo +nightly fmt --all"
depends_post = ["fmt-tooling"]

[tasks.fmt-check]
description = "Check formatting without modifying (nightly rustfmt)"
depends = ["ensure-nightly-rustfmt"]
run = "cargo +nightly fmt --all --check"
depends_post = ["fmt-tooling-check"]

# `cargo fmt --all` formats workspace *members*, and `members = ["crates/*"]` — so no crate under
# `tooling/` has ever been fmt-gated. This is wider than the compile gap `check-dng-real` closes:
# that one is unique to `gamut-dng-real-conformance`, the only excluded crate nothing depends on,
# whereas rustfmt is blind to all fifteen — a path dev-dependency edge makes a crate reachable to
# `clippy --workspace --all-targets`, but does nothing for `fmt --all`. Hung off `fmt`/`fmt-check`
# rather than exposed as a separate gate so the hooks (hk.pkl) and CI inherit it with no second
# call site to keep in sync. rustfmt compiles nothing, so iterating the manifests costs seconds.
# The glob tracks `[workspace].exclude`; a `tooling/` crate promoted to a member would just be
# formatted twice, which is idempotent.
#
# Deliberately *not* `--all` on each manifest: that flag means "all packages, and also their local
# path-based dependencies", so `gamut-dng-real-conformance` alone drags the whole `crates/` tree
# back in — 483 of the 518 files such a loop reaches, every one already covered by the `fmt --all`
# above — and reports one drifting file once per dependent crate that can see it, up to 18 times
# for a single file. Iterating the manifests bare visits each crate's own targets (lib, examples,
# tests) exactly once: the same `tooling/` coverage — set-identical, all 35 files across all 15
# crates — for 35 file-visits instead of 1131.
[tasks.fmt-tooling]
description = "Format the workspace-excluded tooling crates (`fmt --all` stops at the workspace boundary)"
depends = ["ensure-nightly-rustfmt"]
run = "for m in tooling/*/Cargo.toml; do cargo +nightly fmt --manifest-path \"$m\"; done"

[tasks.fmt-tooling-check]
description = "Check formatting of the workspace-excluded tooling crates"
depends = ["ensure-nightly-rustfmt"]
# Deliberately does not stop at the first drifting crate, so one run names every one of them.
run = "fail=0; for m in tooling/*/Cargo.toml; do cargo +nightly fmt --manifest-path \"$m\" --check || fail=1; done; exit $fail"

[tasks.lint]
description = "Lint with Clippy (warnings as errors)"
Expand Down
5 changes: 4 additions & 1 deletion tooling/gamut-dng-real-conformance/examples/gaps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ fn main() {
println!(" preceded by: {:?}", before.map(|s| s.kind));
println!(" followed by: {:?}", after.map(|s| s.kind));
let peek_end = (end as usize).min(start as usize + 24);
println!(" bytes : {:02x?}", &data[start as usize..peek_end]);
println!(
" bytes : {:02x?}",
&data[start as usize..peek_end]
);
if let Ok(text) = std::str::from_utf8(&data[start as usize..peek_end])
&& text.chars().all(|c| c.is_ascii_graphic() || c == '\0')
{
Expand Down
11 changes: 9 additions & 2 deletions tooling/gamut-dng-real-conformance/examples/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,20 @@ fn report_decode(data: &[u8]) {
SubImageData::Undecoded {
compression,
chunks,
} => format!("Undecoded(compression={compression}, {} chunks)", chunks.len()),
} => format!(
"Undecoded(compression={compression}, {} chunks)",
chunks.len()
),
SubImageData::Decoded(v) => format!("Decoded({} samples)", v.len()),
_ => "Other".to_string(),
};
println!(
" kind={:?} {}x{} photometric={} bits={} spp={} -> {payload}",
s.kind, s.dimensions.width, s.dimensions.height, s.photometric, s.bits_per_sample,
s.kind,
s.dimensions.width,
s.dimensions.height,
s.photometric,
s.bits_per_sample,
s.samples_per_pixel,
);
}
Expand Down
9 changes: 7 additions & 2 deletions tooling/gamut-dng-real-conformance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ pub fn corpus_dir() -> PathBuf {
#[must_use]
pub fn manifest() -> Manifest {
let path = corpus_dir().join("MANIFEST.toml");
let text = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
let text =
std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
toml::from_str(&text).unwrap_or_else(|e| panic!("parse {}: {e}", path.display()))
}

Expand All @@ -125,6 +125,10 @@ pub fn manifest() -> Manifest {
/// from the workspace, and the hash is needed for exactly one purpose.
#[must_use]
pub fn sha256(data: &[u8]) -> [u8; 32] {
// FIPS 180-4 round constants and initial hash values, laid out in the spec's own rows.
// Skipped because every element is 11 chars, one over rustfmt's
// `short_array_element_width_threshold`, which would otherwise break them one per line.
#[rustfmt::skip]
const K: [u32; 64] = [
0x428a_2f98, 0x7137_4491, 0xb5c0_fbcf, 0xe9b5_dba5, 0x3956_c25b, 0x59f1_11f1, 0x923f_82a4,
0xab1c_5ed5, 0xd807_aa98, 0x1283_5b01, 0x2431_85be, 0x550c_7dc3, 0x72be_5d74, 0x80de_b1fe,
Expand All @@ -137,6 +141,7 @@ pub fn sha256(data: &[u8]) -> [u8; 32] {
0x748f_82ee, 0x78a5_636f, 0x84c8_7814, 0x8cc7_0208, 0x90be_fffa, 0xa450_6ceb, 0xbef9_a3f7,
0xc671_78f2,
];
#[rustfmt::skip]
let mut h: [u32; 8] = [
0x6a09_e667, 0xbb67_ae85, 0x3c6e_f372, 0xa54f_f53a, 0x510e_527f, 0x9b05_688c, 0x1f83_d9ab,
0x5be0_cd19,
Expand Down
9 changes: 7 additions & 2 deletions tooling/gamut-dng-real-conformance/tests/real_corpus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,13 @@ fn every_real_file_survives_a_preserving_rewrite() {
}
Err(e) => panic!("{name}: open: {e}"),
};
assert!(expect.rewritable, "{name}: opened, but manifest says it must not");
let out = rewrite.write().unwrap_or_else(|e| panic!("{name}: write: {e}"));
assert!(
expect.rewritable,
"{name}: opened, but manifest says it must not"
);
let out = rewrite
.write()
.unwrap_or_else(|e| panic!("{name}: write: {e}"));

assert_eq!(
maker_note_name(out.maker_note),
Expand Down
Loading