Skip to content

feat(coreutils-port): resolve uucore Fluent messages at port time - #2309

Merged
chaliy merged 1 commit into
mainfrom
claude/pensive-hypatia-al4gl9
Aug 18, 2026
Merged

feat(coreutils-port): resolve uucore Fluent messages at port time#2309
chaliy merged 1 commit into
mainfrom
claude/pensive-hypatia-al4gl9

Conversation

@chaliy

@chaliy chaliy commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

What changed

Module mode of bashkit-coreutils-port can now resolve uucore's Fluent
messages at port time, opted in per macro by a new fluent
substitution action in vendored.toml:

[[modules.substitutions]]
prefix = "crate::translate"
action = "fluent"
ftl_sources = ["src/uucore/locales/en-US.ftl", "src/uucore/locales/errors/en-US.ftl"]

Each invocation folds to String::from("…"), or format!("…", …) when the
message carries { $var } slots, with arguments bound by name (a message
may interpolate its slots in a different order than the call lists them). The
use item is dropped, so bashkit still links no Fluent and ships no locale
bundle.

Anything that would bake wrong user-visible text into generated code is a hard
error instead: missing key, slot with no matching argument, argument the message
does not interpolate, non-literal key, and selectors / plurals / term references
(their rendering depends on runtime locale data). Importing the Fluent crate
itself stays rejected under every manifest, and a translate*! call that no
stanza opted in is rejected by the rewriter too, so the import-level gate cannot
be side-stepped by a fully-qualified call.

.ftl parsing moves into a shared ftl.rs now that both modes need it, gaining
pattern splitting for placeable slots.

This PR also carries the regeneration the drift run would have proposed, against
uutils/coreutils@bfd7c63be.

Why

The weekly Coreutils Args Drift workflow has been red on main since
upstream reworked uucore::format. format/mod.rs now imports
error::strip_errno and routes FormatError's Display through Fluent
(12 translate! / translate_text! call sites). Module mode rejected both, so
regeneration aborted before it could build anything:

error: unresolved import: 'crate::error::strip_errno' in
src/uucore/src/lib/features/format/mod.rs: declare a
[[modules.substitutions]] stanza in vendored.toml
##[error]Process completed with exit code 1

Adding a strip_errno shim alone would only have moved the failure to the
Fluent imports, which were a hard error by design. Args mode already resolved
translate! statically, so extending the same approach to module mode is the
root-cause fix rather than a carve-out.

Before / After

Before — regeneration aborts at the first unresolved import; the workflow
never reaches its build or test steps (run 31997016557).

After — the full pipeline runs locally, every gate the workflow uses:

$ cargo run -q -p bashkit-coreutils-port -- port-module <UUTILS> format bfd7c63be
wrote crates/bashkit/src/builtins/generated/format/{argument,num_format,human,spec,mod,escape}.rs
wrote crates/bashkit/src/builtins/generated/{extendedbigdecimal,num_parser}.rs

$ for u in cat ls mktemp od readlink realpath shuf stat tac tee truncate; do ...; done
OK cat / ls / mktemp / od / readlink / realpath / shuf / stat / tac / tee / truncate

$ cargo build -p bashkit                       # clean
$ cargo test -p bashkit --test integration     # 1184 passed
$ BASHKIT_RUN_COREUTILS_DIFF=1 cargo test -p bashkit --test integration -- coreutils_differential_tests::
test result: ok. 22 passed; 0 failed

The differential run used a coreutils multicall built from the same upstream
tree the codegen ran against.

Generated FormatError::fmt, folded from Fluent — no runtime i18n, text
unchanged from the previous pin:

Self::NeedAtLeastOneSpec(s) => {
    ::std::format!("format '{}' has no % directive", String::from_utf8_lossy(s))
}
Self::WrongSpecType => ::std::string::String::from("wrong % directive type was given"),
Self::IoError(e) => ::std::format!("write error: {}", strip_errno(e)),
Self::InvalidCharacter(escape_char, digits, _) => {
    ::std::format!(
        "invalid universal character name \\{}{}", escape_char,
        String::from_utf8_lossy(digits)
    )
}

Regenerated-output diff. The 11 argument surfaces carry no flag drift
only the rev header line. The substantive diff is format/, where upstream
added parse spans to FormatError (SpecError(Vec<u8>, Range<usize>),
MissingHex(Option<Range>), InvalidCharacter(char, Vec<u8>, Option<Range>))
and switched its io-error text to strip_errno, which drops Rust's
(os error N) suffix and so matches GNU. Rendered message text is otherwise
byte-identical.

Regeneration is stable: re-running the port produces byte-identical output.

Risk

  • Low
  • Blast radius is the vendored format/ module, consumed by printf. Its
    spec/differential coverage passes against a same-rev uutils build, and the
    new codegen path fails loudly rather than emitting wrong text — a missing key
    or an unsupported Fluent construct aborts the port.
  • The strip_errno change makes printf's write-error diagnostic match GNU by
    dropping the (os error N) suffix. That is upstream's intent and the only
    user-visible text difference in the regeneration.
  • printf.rs's leak-check test was updated for the new variant arities and now
    covers both spanned and unspanned constructions, keeping TM-INF-022 coverage
    on the span payloads.

Checklist

  • Tests added or updated
  • Backward compatibility considered

Tests: 12 new module-mode tests (folding, name-binding, brace escaping in both
the literal and format! renderings, and each hard-error path), 8 for the
shared ftl.rs, and 1 manifest-schema test. knowledge/runtimes/coreutils-args-port.md
documents the new action and its constraints.


Generated by Claude Code

The weekly Coreutils Args Drift run has been failing since upstream
reworked `uucore::format`: `format/mod.rs` now pulls in
`error::strip_errno` and routes `FormatError`'s `Display` through
Fluent (`translate!` / `translate_text!`, 12 call sites). Module mode
rejected both, so regeneration aborted before it could build anything
and the workflow stayed red on main.

Args mode has always folded `translate!("k")` into a literal from the
utility's `.ftl`. Module mode now does the same, opted in per macro by
a `fluent` substitution naming its message files:

    [[modules.substitutions]]
    prefix = "crate::translate"
    action = "fluent"
    ftl_sources = ["src/uucore/locales/en-US.ftl", ...]

Each invocation folds to `String::from("…")`, or `format!("…", …)` when
the message has `{ $var }` slots, with arguments bound by name since a
message may interpolate its slots in a different order than the call
lists them. The `use` item is dropped: bashkit still links no Fluent
and ships no locale bundle.

Anything that would bake wrong user-visible text into generated code is
a hard error instead: a missing key, a slot with no matching argument,
an argument the message does not interpolate, a non-literal key, and
selectors/plurals/term references, whose rendering depends on runtime
locale data. Importing the Fluent crate itself stays rejected outright
under every manifest. A `translate*!` call that no stanza opted in is
rejected by the rewriter too, so the import-level gate cannot be
side-stepped by a fully-qualified call.

`.ftl` parsing moves to a shared `ftl.rs` now that both modes need it,
gaining pattern splitting for the placeable slots.

Also regenerate against uutils/coreutils@bfd7c63be, which is what the
drift run would have proposed. The 11 argument surfaces carry no flag
drift, only the rev header; the real diff is `format/`, where upstream
added parse spans to `FormatError` and switched its io-error text to
`strip_errno` (dropping Rust's " (os error N)" suffix, matching GNU).
Rendered message text is unchanged.

Verified locally end to end, every gate the drift workflow runs:
regeneration of all 11 arg surfaces plus the vendored module against
upstream HEAD, `cargo build -p bashkit`, the spec tests, a uutils
multicall built from the same tree, and the differential harness with
`BASHKIT_RUN_COREUTILS_DIFF=1` (22 passed). Regeneration is stable:
re-running the port produces byte-identical output.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit f0ff8af Commit Preview URL

Branch Preview URL
Aug 18 2026, 09:38 AM

@chaliy
chaliy merged commit 58b850b into main Aug 18, 2026
43 checks passed
@chaliy
chaliy deleted the claude/pensive-hypatia-al4gl9 branch August 18, 2026 09:52
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