Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions crates/cargo-oxide/src/commands/scaffold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ cuda-core = {{ git = "{GIT_REPO}" }}

fn scaffold_main_rs(async_mode: bool) -> String {
if async_mode {
r#"use cuda_device::{kernel, thread, DisjointSlice};
use cuda_host::cuda_module;
use cuda_async::device_context::init_device_contexts;
r#"use cuda_async::device_context::init_device_contexts;
use cuda_async::device_operation::DeviceOperation;
use cuda_core::LaunchConfig;
use cuda_device::{DisjointSlice, kernel, thread};
use cuda_host::cuda_module;

#[cuda_module]
mod kernels {
Expand Down Expand Up @@ -231,9 +231,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
"#
.to_string()
} else {
r#"use cuda_device::{kernel, launch_bounds, launch_contract, thread, DisjointSlice};
r#"use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig1D};
use cuda_device::{DisjointSlice, kernel, launch_bounds, launch_contract, thread};
use cuda_host::cuda_module;
use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig1D};

#[cuda_module]
mod kernels {
Expand Down
58 changes: 55 additions & 3 deletions crates/cargo-oxide/src/commands/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4065,9 +4065,10 @@ fn scaffold_sync_template_uses_launch_contract_and_docs() {
assert!(files.gitignore.contains("/target/"));
// The template uses the launch_bounds / launch_contract attribute
// macros, so the cuda_device import must bring them in; a scaffolded
// project fails to compile without this exact line.
assert!(files.main_rs.starts_with(
"use cuda_device::{kernel, launch_bounds, launch_contract, thread, DisjointSlice};"
// project fails to compile without these names. The order is rustfmt's,
// pinned by `scaffold_templates_are_rustfmt_clean`.
assert!(files.main_rs.contains(
"use cuda_device::{DisjointSlice, kernel, launch_bounds, launch_contract, thread};"
));
assert!(
files
Expand Down Expand Up @@ -4096,6 +4097,57 @@ fn scaffold_async_template_keeps_async_deps_and_docs() {
assert!(!files.main_rs.contains("use cuda_device::{cuda_module"));
}

/// A scaffolded project must already be `rustfmt`-clean.
///
/// CONTRIBUTING points contributors at `cargo oxide fmt`, so a template that is
/// not canonical makes a brand-new project fail its very first format check on
/// code the user never wrote.
///
/// This runs the real `rustfmt` over the rendered template instead of
/// re-deriving its import ordering here. A hand-written ordering check would
/// agree with a stale template as readily as with a correct one, and would not
/// notice drift anywhere else in the file --
/// `release_depfile_path_matches_real_cargo_uplift_for_hyphenated_bin` shells
/// out to real `cargo` for the same reason.
///
/// The edition is read back out of the scaffolded `Cargo.toml` so this cannot
/// drift from the manifest it is meant to describe.
#[test]
fn scaffold_templates_are_rustfmt_clean() {
for (async_mode, label) in [(false, "sync"), (true, "async")] {
let files = scaffold_files("demo_kernel", async_mode);

let edition = files
.cargo_toml
.lines()
.find_map(|line| line.trim().strip_prefix("edition = "))
.map(|value| value.trim().trim_matches('"').to_string())
.unwrap_or_else(|| panic!("{label} scaffold Cargo.toml declares no edition"));

let root = unique_temp_dir(&format!("cargo_oxide_scaffold_fmt_{label}"));
std::fs::create_dir_all(&root).unwrap();
let main_rs = root.join("main.rs");
std::fs::write(&main_rs, &files.main_rs).unwrap();

// A scaffolded project carries no rustfmt.toml, so plain defaults for
// its edition are exactly what `cargo fmt` applies there.
let check = Command::new("rustfmt")
.args(["--edition", edition.as_str(), "--check"])
.arg(&main_rs)
.output()
.expect("failed to run rustfmt; it is in rust-toolchain.toml's component list");

let diff = String::from_utf8_lossy(&check.stdout).into_owned();
std::fs::remove_dir_all(&root).unwrap();

assert!(
check.status.success() && diff.is_empty(),
"the {label} scaffold template is not rustfmt-clean, so `cargo oxide fmt \
--check` fails in a freshly scaffolded project:\n{diff}"
);
}
}

#[test]
fn scaffold_gitignore_covers_every_clean_artifact_suffix() {
let gitignore = scaffold_gitignore();
Expand Down
4 changes: 2 additions & 2 deletions cuda-oxide-book/getting-started/hello-gpu.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,11 @@ The `--async` flag generates a project with `tokio` and `cuda-async` dependencie
Here's the generated async vecadd template (with minor formatting edits for readability):

```rust
use cuda_device::{kernel, thread, DisjointSlice};
use cuda_host::cuda_module;
use cuda_async::device_context::init_device_contexts;
use cuda_async::device_operation::DeviceOperation;
use cuda_core::LaunchConfig;
use cuda_device::{DisjointSlice, kernel, thread};
use cuda_host::cuda_module;

#[cuda_module]
mod kernels {
Expand Down
Loading