macOS flat-package (.pkg) builder — product archives on any OS.
[build] writes the same shape productbuild emits: a xar archive
holding a Distribution script plus a <name>.pkg/ component directory
with PackageInfo, Bom, and a gzipped odc-cpio Payload. Every layer
was pinned against Apple tooling on macOS 26.5 (pkgbuild/productbuild
output dissected byte-by-byte; results verified with pkgutil --expand-full, lsbom, and installer).
Lower-level pieces are public for reuse and testing: [write_xar]
(xar archives with nested directories) and [write_bom] (Bill of
Materials over a staged tree).
Spike outcomes the implementation rests on:
- xar:
apple-xar0.20 has no turn-key writer, but its primitives compose:TableOfContents/File/FileDataare public withto_xml(),ChecksumType::digest_dataprovides digests, andXarHeaderderives scrollIOwrite. The only wrinkle is thatTableOfContents's inner field is private with no constructor, so we bootstrap one by parsing a minimal template TOC viafrom_readerand mutating it through itsDerefMut<Target = XarToC>impl. No hand-rolled writer needed. - BOM:
apple-bom0.3's shippedBomBuilderis unusable — itsbuild_bom()panics unconditionally, and beneath that lie a dozen more writer bugs (wrong checksum algorithm, wrong B-tree order, missing free list, ...; seebom_builder.rsfor the catalog). Bounded fallback taken:bom_builderports the assembly on top of apple-bom's publicformattypes with those fixes; the output matches bothmkbomand pkgbuild's embeddedBomunderlsbom -p fmugsc(paths, modes, uid, gid, size, cksum). Per-file modes must include the file-type bits (e.g.0o100755). - cpio: the Payload is odc/ASCII cpio (magic
070707), hand-rolled here because the format is a 76-byte octal header and pkgbuild's conventions (sequential inodes, dir nlink = 2 + children, zero pad to a 512-byte boundary) need exact control no crate offered.