Repository navigation
cargo-oxide: make cargo oxide new emit rustfmt-clean projects - #1193
Conversation
Both scaffold templates declared their imports in an order rustfmt does not
produce, so a freshly created project failed `cargo fmt --check` on code the
user never wrote:
$ cargo oxide new demo && cd demo && cargo oxide fmt --check
Diff in src/main.rs:1:
-use cuda_device::{kernel, launch_bounds, launch_contract, thread, DisjointSlice};
-use cuda_host::cuda_module;
use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig1D};
❌ Some files need formatting. Run: cargo oxide fmt
Two orderings were wrong in both templates: the crates were not sorted
(`cuda_device`/`cuda_host` preceded `cuda_core`, and in the async template
also `cuda_async`), and inside the braces `DisjointSlice` sorted after the
lowercase items under the scaffold's `edition = "2024"`. CONTRIBUTING points
contributors at `cargo oxide fmt`, so this is the first thing a new project
is told to run.
Nothing formats these today: the templates are string constants, so the
repository's own fmt job formats the file holding them and never their
contents, and no gate runs `cargo oxide new`.
Both templates now carry rustfmt's own output, pinned by
`scaffold_templates_are_rustfmt_clean`, which runs the real `rustfmt` over
each rendered template and takes the edition from the scaffolded
`Cargo.toml` so the two cannot drift. A re-derived ordering check would have
agreed with the stale template just as readily, and would not have noticed
drift elsewhere in the file.
`scaffold_sync_template_uses_launch_contract_and_docs` asserted the old
unformatted line, so the defect was an asserted invariant; it now asserts the
canonical one and leaves ordering to the new test.
The book's `getting-started/hello-gpu.md` presents one of these listings as
the generated async template, and NVIDIA#548 exists to keep it in step with
`scaffold_main_rs(true)` with no automated guard between them, so it moves
with the scaffold here.
Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
|
Reviewed and validated; nothing to change, this is ready as-is. The root-cause read is right (the templates are string constants, so the repo's own fmt gate formats the file holding the strings and never the text inside them, and the old test asserted the stale line), and the fix follows what Validated on today's main + the new nightly pin (your branch predates the toolchain bump, so this was the open question):
The test shelling out to the real rustfmt instead of re-deriving its rules is the part that keeps this fixed: next nightly that changes canonical form fails the test instead of the user's first |
* docs(book): put the book's Rust blocks on rustfmt's import order #1193 made `cargo oxide new` emit rustfmt-clean projects, because a scaffolded project failing its own first `cargo oxide fmt --check` is a bad first five minutes. The book's hand-written blocks kept the old order, so a reader who copies one into a project gets the same diff on the same command. Measured over every tracked book page, taking each ```rust block that contains a `cuda_*` import and running the pinned rustfmt at the book's edition: already rustfmt-clean 58 differ in imports only <- fixed here 15 differ in other formatting <- left alone 23 The 15 are the same two orderings #1193 fixed in the templates: crates not sorted (`cuda_device`/`cuda_host` ahead of `cuda_core`, and `cuda_async` where present), and `DisjointSlice` and friends after the lowercase items inside the braces rather than before them under style edition 2024. The 23 differ for reasons that have nothing to do with imports -- one parameter per line in a function signature, `{ /* ... */ }` split across two lines, a wrapped call collapsed. Reformatting deliberately laid-out teaching code is a different change with a different argument, so those blocks are untouched. Only rustfmt's import region is spliced back into each block, not its whole formatted output, so every non-import line stays byte-identical. Taking the whole block instead reflows that teaching code, which is what made the distinction worth drawing rather than assuming. Nothing catches this today: a markdown code block is compiled by nothing and formatted by nothing, and `check-book-api-names.sh` verifies that the device-API names the docs show exist, which is a different property and already passes. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com> * docs(book): finish the import-order sweep on the remaining 30 blocks The first commit put 17 Rust blocks in 14 book pages on rustfmt's edition-2024 import order. 30 blocks in 12 pages still carried the old order (crates unsorted, uppercase names after lowercase ones). This splices rustfmt's output for the top-level `use` region into each of them, the same mechanism the first commit used: 16 whole-item blocks and 14 partial snippets whose `use` lines format on their own. Every changed line is a `use` statement or a brace-list continuation; no other line moved, so the one-param-per-line teaching layout stays. Also adds the `DisjointSlice` import the `reduce_dynamic` block in the shared-memory chapter used without importing, then formats that region. With both commits the book has one import order: 47 blocks in 18 pages changed, 0 blocks differ from `rustfmt --edition 2024` in their imports. Signed-off-by: nihalpasham <nihalp@nvidia.com> * Add a status guard for the book's Rust import order A markdown block is compiled by nothing and formatted by nothing, which is how 47 blocks drifted from the order `cargo fmt` writes in an edition-2024 `cargo oxide new` project after the scaffold templates were fixed with a real-rustfmt test. scripts/check-book-import-order.sh walks every tracked ```rust block under cuda-oxide-book/, extracts each contiguous run of top-level `use` items, and runs `rustfmt --edition 2024 --check` on that region alone. It prints the page, the fence line and rustfmt's diff, and exits non-zero. Signatures, bodies and comments are never formatted, so teaching layout and partial snippets with no enclosing item cannot trip it. A `use` line rustfmt cannot parse is reported too. Parse self-test: at least 50 blocks with imports must be found. rustfmt comes from rust-toolchain.toml through the rustup proxy, as in fmt.yml, so the pinned nightly's order is the one checked. On the branch before the sweep the guard listed all 30 stale blocks in 12 pages; after it, 98 import regions in 98 blocks across 42 pages pass. Wired into status-guard.yml as `book-import-order` and into the Justfile's `check-guards` recipe, which mirrors that workflow. Signed-off-by: nihalpasham <nihalp@nvidia.com> --------- Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com> Signed-off-by: nihalpasham <nihalp@nvidia.com> Co-authored-by: nihalpasham <nihalp@nvidia.com>
Closes #1192.
Both
cargo oxide newtemplates emittedusestatements in an orderrustfmtdoes not produce, so a freshly scaffolded project failed its own format check on code the user never wrote. CONTRIBUTING points contributors atcargo oxide fmt, so this is the first thing a new project is told to run.Before / after
Before — sync template, straight after
cargo oxide new demo:Before —
--async, wrong in two places rather than one:After — both templates:
Two independent orderings were wrong
cuda_device/cuda_hostprecededcuda_core, and in the async template alsocuda_async.DisjointSlicetrailed the lowercase items; under the scaffold'sedition = "2024"it sorts first.Why nothing caught it
The templates are string constants, so the repository's own
fmtjob formats the file that holds them and never their contents, and no gate runscargo oxide new.The existing test also pinned the defect in place:
It now asserts the canonical line and leaves ordering to the new test.
The test runs real rustfmt
scaffold_templates_are_rustfmt_cleanrenders each template and runs the realrustfmtover it, taking the edition back out of the scaffoldedCargo.tomlso the check cannot drift from the manifest it describes.I deliberately did not re-derive rustfmt's import ordering in the test: such a check agrees with a stale template exactly as readily as with a correct one, and sees nothing outside the import block.
release_depfile_path_matches_real_cargo_uplift_for_hyphenated_binshells out to realcargofor the same reason, so this follows that precedent. Ifrustfmtis absent the test fails loudly rather than passing quietly; it is inrust-toolchain.toml's component list.Mutations (each applied to the fixed tree, each caught)
The third and fourth matter: they are drift a hand-written ordering check would sail past.
The book moves with it
getting-started/hello-gpu.mdpresents the async listing as "the generated async vecadd template". #548 exists precisely to keep it in step withscaffold_main_rs(true), and nothing automated holds them together, so leaving it would have silently re-opened that issue. Parity is verified by checking the scaffold's rendered import block appears verbatim in the page.The book's other
use cuda_device::{...}blocks are hand-written examples rather than scaffold output, so they are out of scope here — book code blocks are not formatted by anything today, which is a separate gap I am happy to file if wanted.Scope
rustfmtparsing both templates establishes they are still syntactically valid.cargo oxide fmtbuilds the whole codegen backend before formatting anything #1187/cargo-oxide:fmtmust not build the codegen backend #1188, which concernfmtbuilding the backend rather than what the scaffold emits.Test plan
cargo test -p cargo-oxide— 186 passed, 0 failed (185 + the new test).cargo clippy -p cargo-oxide --all-targets -- -D warnings— clean.cargo fmt --check— clean.RUSTDOCFLAGS="-D warnings" cargo doc --no-deps -p cargo-oxide— clean.sphinx-build -W --keep-going -b html cuda-oxide-book— build succeeded.check-book-api-names,check-cli-doc-coverage,check-crate-inventory,check-spdx-headers,check-toolchain-parity,check-test-matrix-coverage,check-error-example-status,check-example-license-policy,check-reserved-prefixes,check-book-catalog-stamp) — all pass.cargo fmt --checkis clean in each.