Skip to content

feat(#83): --archive / -z control surface + libarchive-backed reader - #436

Open
helly25 wants to merge 5 commits into
mainfrom
feature/83-archive-flag
Open

feat(#83): --archive / -z control surface + libarchive-backed reader#436
helly25 wants to merge 5 commits into
mainfrom
feature/83-archive-flag

Conversation

@helly25

@helly25 helly25 commented Aug 9, 2026

Copy link
Copy Markdown
Owner

First archive slice (#83): the ratified control surface, with no diving yet.

--archive[=none|roots|all] plus the short -z- / -z / -z+ (chmod-style suffix signs, the -g gitignore family's spelling). The modes are nested, so one ordered enum expresses two separately-wanted behaviors: none keeps find's opaque-archive behavior, roots dives only into an archive named AS a root, all also dives ones met mid-walk. Bare --archive is all. The find style defaults to none, the xff family to roots.

Four real bugs this surfaced, all fixed with tests

  • The short forms slipped past the extras gate entirely, so -z+ produced a different, later error than --archive (the gate looked up the arg without stripping the suffix sign).
  • --archive=none demanded a rebuild with the archive extra, although asking for find's behavior is exactly what a lean build already does.
  • The old --archive row had no value grammar, so --archive=none was rejected as an unknown option.
  • Every rebuild hint named --//xff:archive, which is not a real flag (it is --//xff:xff_archive). Four sites printed or documented it, and extras_test was asserting the broken string. ExtraBuildFlag() now maps each extra key to its real flag explicitly, because a derivation rule cannot work for both (pcre2 maps to xff_pcre).

The two "no" states are now distinct and unconfusable: no extra -> "no archive support; rebuild with --//xff:xff_archive"; extra present but diving unbuilt -> "not yet implemented". Only an EXPLICIT request trips either, so the xff family's roots default never breaks a plain walk.

Also extracts ReadHashDefaults so RunFind stays under the clang-tidy function-size threshold (the guard pushed it to 802/800 statements).

Tests: xff/cli/archive_test.sh pins every spelling on both sides of the split, the untouched default walk, the documented modes, and that both surfaces name real Bazel flags. XFF.md regenerated.

helly25 added 4 commits August 9, 2026 23:52
First archive slice: the ratified control surface, with no diving yet.

--archive[=none|roots|all] with the short -z-/-z/-z+ (chmod-style suffix signs,
the -g gitignore family's spelling). The modes are nested, so one ordered enum
expresses two separately-wanted behaviors: none keeps find's opaque-archive
behavior, roots dives only into an archive named AS a root, all also dives ones
met mid-walk. Bare --archive is all. The find style defaults to none, the
xff family to roots.

Fixes three real problems in the existing extras gate found while wiring this:
- the short forms slipped past it entirely (-z+ produced a different, later
  error than --archive), because the gate looked up the arg without stripping
  the suffix sign;
- --archive=none demanded a rebuild with the archive extra, although asking for
  find's behavior is exactly what a lean build already does;
- the old --archive row had no value grammar, so --archive=none was rejected as
  an unknown option.

The two "no" states are now distinct and never confusable: a build without the
extra says "no archive support; rebuild with --//xff:archive", while a build
that has it but cannot dive yet says "not yet implemented". Only an EXPLICIT
request trips either, so the xff family's roots default never breaks a plain
walk.

Extracts ReadHashDefaults so RunFind stays under the clang-tidy function-size
threshold (the guard pushed it to 802/800 statements).

Tests: xff/cli/archive_test.sh pins every spelling on both sides of the split,
the untouched default walk, and the documented modes. XFF.md regenerated.
The extras gate and --help=extras derived the rebuild hint as --//xff:<extra>,
but the actual flags carry an xff_ prefix (//xff:xff_archive, //xff:xff_pcre).
Both therefore printed a flag that does not exist, which is worse than no hint.

Adds ExtraBuildFlag() next to ExtraEnabled(), mapping each extra key to its real
flag label explicitly rather than by a derivation rule - the keys and the flags
genuinely differ (pcre2 -> xff_pcre), so any rule would be wrong for one of them.
An unknown extra yields no label and the caller drops the hint instead of
inventing one. archive_test pins both surfaces.
Three more sites still printed or documented the non-existent flag: the help
renderer's not-built note, the --archive flag's own details prose, and a BUILD
comment. extras_test was pinning the wrong string, so it is corrected too (it
asserted the bug rather than the behavior). All four surfaces now name
--//xff:xff_archive, and XFF.md is regenerated.
…ule (#437)

* feat(#83): libarchive-backed archive reader as a standalone extra module

Second archive slice: the dependency and the reader, still with no walk
integration.

extra_modules/archive/ is its own Bazel module (@xff_archive), the same shape
as the PCRE2 extra: it depends on @libarchive (from the BCR, no vendoring) and
the shared @xff_extras_api seams, never back into the xff core, so deleting the
directory drops the extra entirely. Verified: the lean //xff/cli:xff has zero
libarchive in its dep graph.

archive_reader lists an archive's members from memory or by streaming a file.
libarchive detects the format and compression filter from content, so tar / zip
/ cpio / ar and the gz / bz2 / xz / zstd / lz4 filters all come through one
entry point. It reads headers only, never member content, so listing a huge
archive stays cheap.

The error contract matters for the walk and is pinned by tests: "not an
archive" (InvalidArgument) must stay distinct from "corrupt archive"
(DataLoss) - the first is an ordinary file, the second is a real error to
report. Empty input is explicitly not an archive: libarchive opens zero bytes
and reports EOF, which would otherwise make an empty file look like a valid
archive holding nothing.

Licensing: libarchive is BSD-2-Clause and its codec closure (zlib, bzip2,
liblzma, lz4, zstd on its BSD arm) is permissive throughout; mbedtls stays off,
so no crypto arm is pulled. The notice registers itself from the reader's
translation unit through the existing license-notice seam, so --help=notice
and the NOTICE file stay complete by construction.

Tests write real tar and gzip-filtered tar archives with libarchive's write API
and read them back, so there are no committed binary fixtures and the whole
dependency is exercised end to end.

* fix(#83): correct the remaining stale --//xff:archive references

Three more sites still printed or documented the non-existent flag: the help
renderer's not-built note, the --archive flag's own details prose, and a BUILD
comment. extras_test was pinning the wrong string, so it is corrected too (it
asserted the bug rather than the behavior). All four surfaces now name
--//xff:xff_archive, and XFF.md is regenerated.
@helly25 helly25 changed the title feat(#83): --archive / -z control surface + not-implemented guard feat(#83): --archive / -z control surface + libarchive-backed reader Aug 9, 2026
@helly25

helly25 commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Note: this PR now carries both archive slices. The reader PR (#437) was stacked on this branch and auto-merged into it immediately - a PR based on a feature branch has no required checks, so auto-merge fires at once. So review here covers:

  1. the --archive/-z control surface + not-implemented guard (and the four extras-gate bugs it exposed), and
  2. @xff_archive: the standalone libarchive module + member reader.

Full CI runs on this PR since its base is main. The VFS-seam PR (#438) is deliberately NOT auto-merging; it will retarget to main once this merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant