Repository navigation
Vendor uucore::format and migrate printf.rs to use it #1534
Copy link
Copy link
Closed
Description
Background
First consumer of the module-vendor mode tool capability tracked in #1533.
crates/bashkit/src/builtins/printf.rs is ~766 LoC, of which roughly 600 are a handwritten format-specifier parser. uutils has a battle-tested, fuzzed equivalent in src/uucore/src/lib/features/format/. Verification confirmed:
- The
format/module's imports are platform-clean:std,bigdecimal,num-traits,unit-prefix,os_display, and a handful of uucore-internal types (extendedbigdecimal,UError,NonUtf8OsStrError). Norustix,fluent, oricu_localecome from insideformat/. FormatError'sDisplayimpl uses Display-only formatting (no{:?}) — TM-INF-022 clean by inspection.format/has no width/precision DoS caps:format/num_format.rscallsString::with_capacity(precision)anditer::repeat_n('0', N)without bounds. A wrapper enforcing the existingMAX_FORMAT_WIDTH = 10_000cap is mandatory.
This issue is the end-to-end use of #1533: register format/ in the vendoring manifest, generate the module, migrate printf.rs, prove the build matrix.
Acceptance criteria
- Depends on Add module-vendor mode to bashkit-coreutils-port #1533 (module-vendor mode merged first).
format/registered in the vendoring manifest introduced by Add module-vendor mode to bashkit-coreutils-port #1533, pinned to the same uutils revision the args codegen currently uses.crates/bashkit/src/builtins/generated/format/produced byport-module, committed to the repo.- Internal-type substitutions documented in the manifest:
extendedbigdecimal→ inline (small file)UError→ replace with bashkit'scrate::error::Erroror stripped if only used for error-propagation traits we don't needNonUtf8OsStrError→ inline or replace with a local equivalent
- New direct deps in
crates/bashkit/Cargo.toml:bigdecimal,num-traits,unit-prefix,os_display. Versions aligned to uutils' workspace pins. - Width/precision wrapper:
- Pre-validates the format string before calling
sprintf. Suggested implementation: iterateparse_spec_only(...), reject any spec wherewidthorprecisionexceedsMAX_FORMAT_WIDTH. - Wrapper lives in
printf.rs(or a small helper module underbuiltins/) — not inside the generatedformat/directory, so regenerating the module never overwrites the cap. - Unit tests cover: width over cap, precision over cap, asterisk-resolved width over cap (
%*swith arg999_999), nested sub-spec ignoring cap (verify wrapper still catches it).
- Pre-validates the format string before calling
crates/bashkit/src/builtins/printf.rsswitches its format-spec parsing to call intosuper::generated::format::sprintf(...)(or chosen entry point). Handwritten format-spec parsing is removed; body shrinks to VFS/stdout glue.- TM-INF-022: a new unit test exercises every
FormatErrorvariant viabashkit::testing::assert_no_leakto confirm Display-only output. - All existing
printfspec tests stay green (tests/spec_cases/bash/printf*.test.shand any inline tests). - Build matrix:
cargo build -p bashkit --release— green.cargo build -p bashkit --target wasm32-unknown-unknown— green (this is the whole point — uucore-as-runtime-dep broke this; vendor must preserve it).cargo build -p bashkit --no-default-features— green.cargo clippy --workspace --all-targets --all-features -- -D warnings— green.
- Release build time on
bashkit-clidoes not regress beyond +20 s vs currentmain. The probe-measured uucore-runtime cost was +98 s; vendor-only cost should be a fraction. specs/coreutils-args-port.md"Vendored modules" section (introduced by Add module-vendor mode to bashkit-coreutils-port #1533) gets aformatentry with the substitution decisions used.
Verification artefacts (probe branch)
| Metric | Baseline | uucore runtime dep | Delta |
|---|---|---|---|
bashkit-cli size |
24,260,000 B | 24,269,344 B | +9 KB (declared, unused) |
| Cold release build time | 1m 37s | 3m 15s | +98 s |
wasm32-unknown-unknown build |
✅ pass | ❌ fail (errno) | hard blocker |
This issue should restore wasm ✅, deliver real format parity, and add only the four small numeric-deps' compile cost.
Out of scope
- Adding the module-vendor mode (covered by Add module-vendor mode to bashkit-coreutils-port #1533).
- Vendoring other modules (
ranges,parser-num,version-cmp,quoting-style) — separate consumer issues. - Differential testing for printf — covered by Differential testing harness for ported coreutils builtins (vs. uutils binaries) #1530's harness; printf gains a fixture row after this lands.
- Changing the public
Spec/FormatArgumentAPI beyond what bashkit needs.
Open questions
- Width-cap location. Inside the wrapper in
printf.rs(recommended — generated module stays untouched on regen) vs as a patch applied by the codegen tool itself (more invasive, easier to forget). Recommend the wrapper. - Internal-type substitutions. Whether
UErroris needed at all onceformat/is divorced from uucore's wider error machinery, or whether a tiny localErrorshim is enough. Decide during the port — the manifest captures the decision.
Related
- Add module-vendor mode to bashkit-coreutils-port #1533 — module-vendor mode (blocker)
- feat(builtins): port uutils argument surfaces via codegen (POC: cat, tac) #1529 — codegen POC (args mode)
- Differential testing harness for ported coreutils builtins (vs. uutils binaries) #1530 — differential testing harness (printf row added after this)
specs/coreutils-args-port.mdcrates/bashkit/src/builtins/printf.rscrates/bashkit-coreutils-port/
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
feat(coreutils-port): add module-vendor mode with manifest and drift CI
feat(coreutils-port): add module-vendor mode with manifest and drift CI
feat(coreutils-port): add module-vendor mode with manifest and drift …
feat(coreutils-port): implement inline action
chaliy commentedon May 8, 2026
Picking up #1534. PR #1608 lands the rewriter prerequisite (both
replace_withandinlineactions). The format vendor + printf migration land in follow-ups.Scope correction
The issue body lists three uucore-internal substitutions:
extendedbigdecimal(inline),UError(replace_with),NonUtf8OsStrError(inline-or-local). Surveyingsrc/uucore/src/lib/features/format/at the pinned39364b6rev shows the actual import surface is wider:mod.rscrate::extendedbigdecimal::ExtendedBigDecimal,crate::NonUtf8OsStrError,crate::error::UErrorargument.rscrate::format::spec::ArgumentLocation,crate::error::set_exit_code,crate::os_str_as_bytes,crate::parser::num_parser::{ExtendedParser, ExtendedParserError},crate::quoting_style::{QuotingStyle, locale_aware_escape_name},crate::show_error,crate::show_warning,super::ExtendedBigDecimalescape.rscrate::format::FormatError(self-ref)human.rsnum_format.rsspec.rscrate::format::FormatArguments(self-ref),crate::os_str_as_bytes,crate::quoting_style::{QuotingStyle, locale_aware_escape_name}So the manifest needs ~8 substitution stanzas, not 3. Proposed mapping (subject to revision once each is touched):
crate::extendedbigdecimalbigdecimal/num-traitsonlycrate::NonUtf8OsStrErrorcrate::error::UErrorcrate::error::Error(or local stub)crate::error::set_exit_codecrate::os_str_as_bytescrate::quoting_styleQuotingStyle,locale_aware_escape_name)crate::parser::num_parser::{ExtendedParser, ExtendedParserError}%d/%i/%e; pulls in extendedbigdecimalcrate::show_error,crate::show_warningeprintln!macroscrate::format::*self-refs need no substitution: they resolve to the vendored module's own siblings.Plan for the next PR (after #1608 merges)
bigdecimal,num-traits,unit-prefix,os_displaytocrates/bashkit/Cargo.toml— verify wasm32 builds first since these are the bulk of Vendor uucore::format and migrate printf.rs to use it #1534's risk.vendored.toml. Implement bashkit-side stubs forquoting_style,set_exit_code,show_error,show_warning.cargo run -p bashkit-coreutils-port -- port-module <UUTILS_DIR> format 39364b6. Commit the generated tree undercrates/bashkit/src/builtins/generated/format/plus the inlined siblings.printf.rstobashkit::builtins::generated::format::sprintf. Width/precision wrapper sits inprintf.rs, not in the generated module, so regen never wipes it.assert_no_leaktest for everyFormatErrorvariant.cargo build -p bashkit --release,cargo build -p bashkit --target wasm32-unknown-unknown,cargo build -p bashkit --no-default-features, full clippy.This is sized for two more PRs (substitution stubs + vendor; printf migration + tests). Each is reviewable on its own.
Generated by Claude Code
feat(bashkit): add bigdecimal/num-traits/unit-prefix/os_display deps
feat(coreutils-port): implement replace_with rewriter (towards #1534)
feat(coreutils-port): implement inline action
feat(bashkit): add bigdecimal/num-traits/unit-prefix/os_display deps
feat(coreutils-port): add module-vendor mode with manifest and drift …