Fix CI: pin Rust 1.99, newer clippy lints, Windows/macOS test portability, NetBSD libexecinfo for cross

This commit is contained in:
Andy Dixon
2026-10-08 20:32:29 +01:00
parent e3bfc28d71
commit 82dfab3dff
10 changed files with 47 additions and 18 deletions
+11
View File
@@ -0,0 +1,11 @@
# The cross image for NetBSD lacks libexecinfo, which Rust's standard library
# links on NetBSD. Take it (and libelf, which it needs) from the official
# NetBSD 9.2 base set, matching the image's libc, checked against the
# release's published SHA512.
[target.x86_64-unknown-netbsd]
pre-build = [
"curl -fsSL -o /tmp/base.tar.xz https://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.2/amd64/binary/sets/base.tar.xz",
"echo '961511ec63f4803529aeb057632e2b8cf87e73bfdfed445d7521b607f573245359163b9f6b6a158fa80de7a994cf3727af384ec42ac8d9ccdd59a87197a41b55 /tmp/base.tar.xz' | sha512sum -c",
"tar -xJf /tmp/base.tar.xz -C /usr/local/x86_64-unknown-netbsd --strip-components=2 ./usr/lib/libexecinfo.so ./usr/lib/libexecinfo.so.0 ./usr/lib/libexecinfo.so.0.0 ./usr/lib/libelf.so ./usr/lib/libelf.so.2 ./usr/lib/libelf.so.2.0",
"rm /tmp/base.tar.xz",
]
+4 -3
View File
@@ -1,7 +1,8 @@
# Development # Development
Requires a stable Rust toolchain (`rustup`); the dependencies are pinned in Requires `rustup`: `rust-toolchain.toml` pins the compiler (so local builds
`Cargo.lock`. No C toolchain beyond what Rust itself needs. match CI), and `Cargo.lock` pins the dependencies. No C toolchain beyond what
Rust itself needs.
```sh ```sh
make build # target/release/mapsize make build # target/release/mapsize
@@ -27,7 +28,7 @@ binaries under qemu.
| Linux | amd64, arm64, 386, arm (v7), riscv64 | `*-unknown-linux-musl` (static) | cargo-zigbuild + zig | | Linux | amd64, arm64, 386, arm (v7), riscv64 | `*-unknown-linux-musl` (static) | cargo-zigbuild + zig |
| Windows | amd64, arm64 | `x86_64-pc-windows-gnu`, `aarch64-pc-windows-gnullvm` | cargo-zigbuild + zig | | Windows | amd64, arm64 | `x86_64-pc-windows-gnu`, `aarch64-pc-windows-gnullvm` | cargo-zigbuild + zig |
| macOS | amd64, arm64 | `*-apple-darwin` | cargo on a Mac (or zigbuild) | | macOS | amd64, arm64 | `*-apple-darwin` | cargo on a Mac (or zigbuild) |
| FreeBSD, NetBSD | amd64 | `x86_64-unknown-{freebsd,netbsd}` | cross + Docker | | FreeBSD, NetBSD | amd64 | `x86_64-unknown-{freebsd,netbsd}` | cross + Docker (`Cross.toml` adds NetBSD's libexecinfo) |
Platform differences live in `src/platform.rs` (and `cfg` blocks where the Platform differences live in `src/platform.rs` (and `cfg` blocks where the
terminal code needs them); nothing else switches on the OS. terminal code needs them); nothing else switches on the OS.
+4
View File
@@ -0,0 +1,4 @@
[toolchain]
channel = "1.99.0"
components = ["rustfmt", "clippy"]
profile = "minimal"
+1 -1
View File
@@ -110,7 +110,7 @@ fn interrupt_cancel() -> Cancel {
} }
} }
let c = CANCEL.get_or_init(Cancel::new).clone(); let c = CANCEL.get_or_init(Cancel::new).clone();
unsafe { libc::signal(libc::SIGINT, on_sigint as libc::sighandler_t) }; unsafe { libc::signal(libc::SIGINT, on_sigint as *const () as libc::sighandler_t) };
c c
} }
+1 -1
View File
@@ -492,7 +492,7 @@ mod tests {
assert_eq!(top.len(), 3); assert_eq!(top.len(), 3);
assert_eq!(tr.node(top[0]).size, 99); assert_eq!(tr.node(top[0]).size, 99);
assert_eq!(tr.node(top[2]).size, 97); assert_eq!(tr.node(top[2]).size, 97);
assert_eq!(tr.path_string(top[0]), "/r/d/f"); assert_eq!(tr.path_string(top[0]).replace('\\', "/"), "/r/d/f");
} }
#[test] #[test]
+6 -3
View File
@@ -916,15 +916,18 @@ pub(crate) mod tests {
fn hostile_names_preserved() { fn hostile_names_preserved() {
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
let root = TempDir::new("names"); let root = TempDir::new("names");
let names: [&[u8]; 6] = [ #[allow(unused_mut)]
let mut names: Vec<&[u8]> = vec![
"日本語.txt".as_bytes(), "日本語.txt".as_bytes(),
"emoji-🎉.png".as_bytes(), "emoji-🎉.png".as_bytes(),
b"esc\x1b[31mred", b"esc\x1b[31mred",
b"nl\nname", b"nl\nname",
b"sp ace", b"sp ace",
b"bad\xffutf8",
]; ];
for n in names { // APFS rejects names that are not valid UTF-8.
#[cfg(not(target_os = "macos"))]
names.push(b"bad\xffutf8");
for &n in &names {
std::fs::write(root.0.join(std::ffi::OsStr::from_bytes(n)), b"x").unwrap(); std::fs::write(root.0.join(std::ffi::OsStr::from_bytes(n)), b"x").unwrap();
} }
let t = run_scan( let t = run_scan(
+14 -4
View File
@@ -686,8 +686,11 @@ impl JsonTime {
if self.secs >= 0 { if self.secs >= 0 {
UNIX_EPOCH + Duration::new(self.secs as u64, self.nanos) UNIX_EPOCH + Duration::new(self.secs as u64, self.nanos)
} else { } else {
UNIX_EPOCH - Duration::from_secs(self.secs.unsigned_abs()) // Windows cannot represent times before 1601 (such as the zero
+ Duration::from_nanos(self.nanos as u64) // time, year 1); those clamp to the Unix epoch.
UNIX_EPOCH
.checked_sub(Duration::from_secs(self.secs.unsigned_abs()))
.map_or(UNIX_EPOCH, |t| t + Duration::from_nanos(self.nanos as u64))
} }
} }
@@ -897,7 +900,10 @@ mod tests {
assert_eq!(d.removed.len(), 1); assert_eq!(d.removed.len(), 1);
assert_eq!(&d.old.node(d.removed[0]).name[..], b"b"); assert_eq!(&d.old.node(d.removed[0]).name[..], b"b");
let ch = d.changes(&nw, 10, al); let ch = d.changes(&nw, 10, al);
let paths: HashMap<&str, Status> = ch.iter().map(|c| (c.path.as_str(), c.status)).collect(); let paths: HashMap<String, Status> = ch
.iter()
.map(|c| (c.path.replace('\\', "/"), c.status))
.collect();
assert_eq!(paths.get("/srv/a/x.iso"), Some(&Status::Grew), "{ch:?}"); assert_eq!(paths.get("/srv/a/x.iso"), Some(&Status::Grew), "{ch:?}");
assert_eq!(paths.get("/srv/b"), Some(&Status::Removed), "{ch:?}"); assert_eq!(paths.get("/srv/b"), Some(&Status::Removed), "{ch:?}");
assert_eq!(paths.get("/srv/c"), Some(&Status::Added), "{ch:?}"); assert_eq!(paths.get("/srv/c"), Some(&Status::Added), "{ch:?}");
@@ -970,6 +976,7 @@ mod tests {
Some(JsonTime::default()) Some(JsonTime::default())
); );
assert_eq!(JsonTime::default().format(), "0001-01-01T00:00:00Z"); assert_eq!(JsonTime::default().format(), "0001-01-01T00:00:00Z");
#[cfg(not(windows))]
assert_eq!( assert_eq!(
JsonTime::from_system(JsonTime::default().to_system()), JsonTime::from_system(JsonTime::default().to_system()),
JsonTime::default() JsonTime::default()
@@ -1010,7 +1017,10 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!(t.node(iso).size, 10000); assert_eq!(t.node(iso).size, 10000);
assert_eq!(t.ext_name(t.node(iso)), "iso"); assert_eq!(t.ext_name(t.node(iso)), "iso");
assert!(t.path_string(iso).ends_with("/tree/a/x.iso")); assert!(t
.path_string(iso)
.replace('\\', "/")
.ends_with("/tree/a/x.iso"));
// And it survives a save/load round trip unchanged. // And it survives a save/load round trip unchanged.
let again = load(&saved(&t)[..]).unwrap(); let again = load(&saved(&t)[..]).unwrap();
assert_eq!( assert_eq!(
+1 -1
View File
@@ -92,7 +92,7 @@ impl Model {
.map(|(i, &v)| (Category::from_u8(i as u8), v)) .map(|(i, &v)| (Category::from_u8(i as u8), v))
.collect(); .collect();
let total: i64 = cats.iter().map(|c| c.1).sum(); let total: i64 = cats.iter().map(|c| c.1).sum();
cats.sort_by(|a, b| b.1.cmp(&a.1)); cats.sort_by_key(|c| std::cmp::Reverse(c.1));
for &(c, v) in &cats { for &(c, v) in &cats {
if y >= end - 8 { if y >= end - 8 {
break; break;
+2
View File
@@ -590,6 +590,8 @@ fn nesting_reaches_deep_media() {
// Headless, o shows the selected folder in the map instead of launching a // Headless, o shows the selected folder in the map instead of launching a
// file manager. // file manager.
// macOS and Windows always have a file manager, so are never headless.
#[cfg(not(any(target_os = "macos", windows)))]
#[test] #[test]
fn reveal_headless_zooms_map() { fn reveal_headless_zooms_map() {
std::env::set_var("DISPLAY", ""); std::env::set_var("DISPLAY", "");
+3 -5
View File
@@ -462,11 +462,9 @@ impl TopView {
val.insert(id, n.size - n.alloc); val.insert(id, n.size - n.alloc);
} }
} }
TopMode::Hardlinks => { TopMode::Hardlinks if n.has(FLAG_HARDLINKED) => {
if n.has(FLAG_HARDLINKED) { tk.offer(id, n.size);
tk.offer(id, n.size); val.insert(id, n.size);
val.insert(id, n.size);
}
} }
_ => {} _ => {}
} }