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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ default-run = "bevy"
name = "bevy"
path = "src/bin/main.rs"

# Integrates custom lints with `rustc`
[[bin]]
name = "bevy_lint_driver"
path = "src/bin/lint_driver.rs"

[dependencies]
# CLI argument parsing
clap = { version = "4.5.16", features = ["derive"] }
Expand All @@ -28,6 +23,3 @@ anyhow = "1.0.86"

# Generates new Bevy projects from templates
cargo-generate = "0.21.3"

# Bevy-specific lints
bevy_lint = { version = "0.1.0-dev", path = "./bevy_lint" }
13 changes: 13 additions & 0 deletions bevy_lint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ name = "bevy_lint"
version = "0.1.0-dev"
edition = "2021"
license = "MIT OR Apache-2.0"
default-run = "bevy_lint"

[[bin]]
name = "bevy_lint"
path = "src/bin/main.rs"

# Integrates custom lints with `rustc`.
[[bin]]
name = "bevy_lint_driver"
path = "src/bin/driver.rs"

[dependencies]
anyhow = "1.0.86"

# Contains a series of useful utilities when writing lints. The version and commit were chosen to
# work with the currently pinned nightly Rust version. When the Rust version changes, this too
Expand Down
17 changes: 17 additions & 0 deletions bevy_lint/src/bin/driver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Enables linking to `rustc` crates.
#![feature(rustc_private)]

extern crate rustc_driver;
extern crate rustc_span;

use bevy_lint::BevyLintCallback;
use rustc_span::ErrorGuaranteed;

fn main() -> Result<(), ErrorGuaranteed> {
// The arguments are formatted as `[DRIVER_PATH, RUSTC_PATH, ARGS...]`. We skip the driver path
// so that `RunCompiler` just sees `rustc`'s path.
let args: Vec<String> = std::env::args().skip(1).collect();

// Call the compiler with our custom callback.
rustc_driver::RunCompiler::new(&args, &mut BevyLintCallback).run()
}
38 changes: 38 additions & 0 deletions bevy_lint/src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use anyhow::{anyhow, ensure, Context};
use std::{env, process::Command};

fn main() -> anyhow::Result<()> {
// The `bevy_lint` lives in the same folder as `bevy_lint_driver`, so we can easily find it
// using the path of the current executable.
let mut driver_path = env::current_exe()
.context("Failed to retrieve the path to the current executable.")?
.parent()
.ok_or(anyhow!("Path to file must have a parent."))?
.join("bevy_lint_driver");

#[cfg(target_os = "windows")]
driver_path.set_extension("exe");

ensure!(
driver_path.exists(),
"Could not find `bevy_lint_driver` at {driver_path:?}, please ensure it is installed!",
);

// Convert the local path to the absolute path. We don't want `rustc` getting
// confused! `canonicalize()` requires for the path to exist, so we do it after the nice error
// message.
driver_path = driver_path.canonicalize()?;

// Run `cargo check`.
let status = Command::new("cargo")
.arg("check")
// This instructs `rustc` to call `bevy_lint_driver` instead of its default routine.
// This lets us register custom lints.
.env("RUSTC_WORKSPACE_WRAPPER", driver_path)
.status()
.context("Failed to spawn `cargo check`.")?;

ensure!(status.success(), "Check failed with non-zero exit code.");

Ok(())
}
17 changes: 0 additions & 17 deletions bevy_lint/src/driver.rs

This file was deleted.

3 changes: 2 additions & 1 deletion bevy_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ extern crate rustc_driver;
extern crate rustc_interface;

mod callback;
pub mod driver;

pub use self::callback::BevyLintCallback;
12 changes: 0 additions & 12 deletions src/bin/lint_driver.rs

This file was deleted.

13 changes: 11 additions & 2 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fn main() -> Result<()> {
Subcommands::New(new) => {
bevy_cli::template::generate_template(&new.name, new.template.as_deref())?;
}
Subcommands::Lint => bevy_cli::lint::lint()?,
Subcommands::Lint { args } => bevy_cli::lint::lint(args)?,
}

Ok(())
Expand All @@ -32,7 +32,16 @@ pub enum Subcommands {
/// Create a new Bevy project from a specified template.
New(NewArgs),
/// Check the current project using Bevy-specific lints.
Lint,
///
/// This command requires `bevy_lint` to be installed, and will fail if it is not. Please see
/// <https://github.com/TheBevyFlock/bevy_cli> for installation instructions.
///
/// To see the full list of options, run `bevy lint -- --help`.
Lint {
/// A list of arguments to be passed to `bevy_lint`.
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},
}

/// Arguments for creating a new Bevy project.
Expand Down
69 changes: 42 additions & 27 deletions src/lint.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
use anyhow::{anyhow, ensure, Context};
use std::{env, process::Command};
use std::{env, path::PathBuf, process::Command};

pub fn lint() -> anyhow::Result<()> {
// The `bevy` CLI lives in the same folder as `bevy_lint_driver`, so we can easily find it
// using the path of the current executable.
let mut driver_path = env::current_exe()
.context("Failed to retrieve the path to the current executable.")?
.parent()
.ok_or(anyhow!("Path to file must have a parent."))?
.join("bevy_lint_driver");
/// Runs `bevy_lint`, if it is installed, with the given arguments.
///
/// Calling `lint(vec!["--workspace"])` is equivalent to calling `bevy_lint --workspace` in the
/// terminal. This will run [`find_bevy_lint()`] to locate `bevy_lint`.
pub fn lint(args: Vec<String>) -> anyhow::Result<()> {
let bevy_lint_path = find_bevy_lint()?;

#[cfg(target_os = "windows")]
driver_path.set_extension("exe");
let status = Command::new(bevy_lint_path).args(args).status()?;

ensure!(
driver_path.exists(),
"Could not find `bevy_lint_driver` at {driver_path:?}, please ensure it is installed!",
status.success(),
"`bevy_lint` exited with a non-zero exit code."
);

// Convert the local path to the absolute path. We don't want `rustc` getting
// confused! `canonicalize()` requires for the path to exist, so we do it after the nice error
// message.
driver_path = driver_path.canonicalize()?;
Ok(())
}

// Run `cargo check`.
let status = Command::new("cargo")
.arg("check")
// This instructs `rustc` to call `bevy_lint_driver` instead of its default routine.
// This lets us register custom lints.
.env("RUSTC_WORKSPACE_WRAPPER", driver_path)
.status()
.context("Failed to spawn `cargo check`.")?;
/// Tries the find the path to `bevy_lint`, if it is installed.
///
/// The current strategy will find a file named `bevy_lint(.exe)` within the same directory as the
/// current executable, which is usually `~/.cargo/bin` or `target/debug`. It will **not** search
/// the `PATH`.
pub fn find_bevy_lint() -> anyhow::Result<PathBuf> {
let mut bevy_lint_path = env::current_exe()
.context("Failed to retrieve the path to the current executable.")?
.parent()
.ok_or(anyhow!("Path to file must have a parent."))?
.join("bevy_lint");

#[cfg(target_os = "windows")]
bevy_lint_path.set_extension("exe");

ensure!(status.success(), "Check failed with non-zero exit code.");
if cfg!(debug_assertions) {
ensure!(
bevy_lint_path.exists(),
"`bevy_lint` could not be found at {}. Please run `cargo build -p bevy_lint` first!",
bevy_lint_path.display(),
);
} else {
ensure!(
bevy_lint_path.exists(),
"`bevy_lint` could not be found at {}. Please follow the instructions at <https://github.com/TheBevyFlock/bevy_cli> to install it.",
bevy_lint_path.display(),
);
}

Ok(())
bevy_lint_path = bevy_lint_path.canonicalize()?;

Ok(bevy_lint_path)
}