Skip to content

Commit 1ba7821

Browse files
committed
Auto merge of rust-lang#12680 - Alexendoo:cargo-dev-setup-toolchain, r=llogiq
Add `cargo dev setup toolchain` Adds a `cargo dev setup toolchain` subcommand that creates a rustup toolchain with symlinks to the local `cargo-clippy` and `clippy-driver`. Allows you to then do `cargo +clippy clippy` in other projects to run the locally built Clippy Sometimes more convenient when you're testing changes on a separate project than `cd`ing back & forth to use `cargo dev lint [project]` changelog: none
2 parents f5e2501 + 1fff3be commit 1ba7821

File tree

7 files changed

+131
-29
lines changed

7 files changed

+131
-29
lines changed

book/src/development/adding_lints.md

+14-12
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ because that's clearly a non-descriptive name.
1818
- [Cargo lints](#cargo-lints)
1919
- [Rustfix tests](#rustfix-tests)
2020
- [Testing manually](#testing-manually)
21-
- [Running directly](#running-directly)
2221
- [Lint declaration](#lint-declaration)
2322
- [Lint registration](#lint-registration)
2423
- [Lint passes](#lint-passes)
@@ -176,23 +175,26 @@ the tests.
176175

177176
Manually testing against an example file can be useful if you have added some
178177
`println!`s and the test suite output becomes unreadable. To try Clippy with
179-
your local modifications, run
178+
your local modifications, run the following from the Clippy directory:
180179

181-
```
180+
```bash
182181
cargo dev lint input.rs
183182
```
184183

185-
from the working copy root. With tests in place, let's have a look at
186-
implementing our lint now.
184+
To run Clippy on an existing project rather than a single file you can use
185+
186+
```bash
187+
cargo dev lint /path/to/project
188+
```
189+
190+
Or set up a rustup toolchain that points to the local Clippy binaries
187191

188-
## Running directly
192+
```bash
193+
cargo dev setup toolchain
189194

190-
While it's easier to just use `cargo dev lint`, it might be desirable to get
191-
`target/release/cargo-clippy` and `target/release/clippy-driver` to work as well in some cases.
192-
By default, they don't work because clippy dynamically links rustc. To help them find rustc,
193-
add the path printed by`rustc --print target-libdir` (ran inside this workspace so that the rustc version matches)
194-
to your library search path.
195-
On linux, this can be done by setting the `LD_LIBRARY_PATH` environment variable to that path.
195+
# Then in `/path/to/project` you can run
196+
cargo +clippy clippy
197+
```
196198

197199
## Lint declaration
198200

clippy_dev/src/fmt.rs

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ struct FmtContext {
3535
}
3636

3737
// the "main" function of cargo dev fmt
38-
#[allow(clippy::missing_panics_doc)]
3938
pub fn run(check: bool, verbose: bool) {
4039
fn try_run(context: &FmtContext) -> Result<bool, CliError> {
4140
let mut success = true;

clippy_dev/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
unused_lifetimes,
1010
unused_qualifications
1111
)]
12+
#![allow(clippy::missing_panics_doc)]
1213

1314
// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate.
1415
#[allow(unused_extern_crates)]

clippy_dev/src/main.rs

+40-15
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ fn main() {
4646
}
4747
},
4848
Some(("setup", sub_command)) => match sub_command.subcommand() {
49+
Some(("git-hook", matches)) => {
50+
if matches.get_flag("remove") {
51+
setup::git_hook::remove_hook();
52+
} else {
53+
setup::git_hook::install_hook(matches.get_flag("force-override"));
54+
}
55+
},
4956
Some(("intellij", matches)) => {
5057
if matches.get_flag("remove") {
5158
setup::intellij::remove_rustc_src();
@@ -57,12 +64,12 @@ fn main() {
5764
);
5865
}
5966
},
60-
Some(("git-hook", matches)) => {
61-
if matches.get_flag("remove") {
62-
setup::git_hook::remove_hook();
63-
} else {
64-
setup::git_hook::install_hook(matches.get_flag("force-override"));
65-
}
67+
Some(("toolchain", matches)) => {
68+
setup::toolchain::create(
69+
matches.get_flag("force"),
70+
matches.get_flag("release"),
71+
matches.get_one::<String>("name").unwrap(),
72+
);
6673
},
6774
Some(("vscode-tasks", matches)) => {
6875
if matches.get_flag("remove") {
@@ -210,6 +217,19 @@ fn get_clap_config() -> ArgMatches {
210217
.about("Support for setting up your personal development environment")
211218
.arg_required_else_help(true)
212219
.subcommands([
220+
Command::new("git-hook")
221+
.about("Add a pre-commit git hook that formats your code to make it look pretty")
222+
.args([
223+
Arg::new("remove")
224+
.long("remove")
225+
.action(ArgAction::SetTrue)
226+
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
227+
Arg::new("force-override")
228+
.long("force-override")
229+
.short('f')
230+
.action(ArgAction::SetTrue)
231+
.help("Forces the override of an existing git pre-commit hook"),
232+
]),
213233
Command::new("intellij")
214234
.about("Alter dependencies so Intellij Rust can find rustc internals")
215235
.args([
@@ -225,18 +245,23 @@ fn get_clap_config() -> ArgMatches {
225245
.conflicts_with("remove")
226246
.required(true),
227247
]),
228-
Command::new("git-hook")
229-
.about("Add a pre-commit git hook that formats your code to make it look pretty")
248+
Command::new("toolchain")
249+
.about("Install a rustup toolchain pointing to the local clippy build")
230250
.args([
231-
Arg::new("remove")
232-
.long("remove")
233-
.action(ArgAction::SetTrue)
234-
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
235-
Arg::new("force-override")
236-
.long("force-override")
251+
Arg::new("force")
252+
.long("force")
237253
.short('f')
238254
.action(ArgAction::SetTrue)
239-
.help("Forces the override of an existing git pre-commit hook"),
255+
.help("Override an existing toolchain"),
256+
Arg::new("release")
257+
.long("release")
258+
.short('r')
259+
.action(ArgAction::SetTrue)
260+
.help("Point to --release clippy binaries"),
261+
Arg::new("name")
262+
.long("name")
263+
.default_value("clippy")
264+
.help("The name of the created toolchain"),
240265
]),
241266
Command::new("vscode-tasks")
242267
.about("Add several tasks to vscode for formatting, validation and testing")

clippy_dev/src/new_lint.rs

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ impl<T> Context for io::Result<T> {
3636
/// # Errors
3737
///
3838
/// This function errors out if the files couldn't be created or written to.
39-
#[allow(clippy::missing_panics_doc)]
4039
pub fn create(
4140
pass: &String,
4241
lint_name: Option<&String>,

clippy_dev/src/setup/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod git_hook;
22
pub mod intellij;
3+
pub mod toolchain;
34
pub mod vscode;
45

56
use std::path::Path;

clippy_dev/src/setup/toolchain.rs

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use std::env::consts::EXE_SUFFIX;
2+
use std::env::current_dir;
3+
use std::ffi::OsStr;
4+
use std::fs;
5+
use std::path::{Path, PathBuf};
6+
use walkdir::WalkDir;
7+
8+
use super::verify_inside_clippy_dir;
9+
10+
pub fn create(force: bool, release: bool, name: &str) {
11+
if !verify_inside_clippy_dir() {
12+
return;
13+
}
14+
15+
let rustup_home = std::env::var("RUSTUP_HOME").unwrap();
16+
let toolchain = std::env::var("RUSTUP_TOOLCHAIN").unwrap();
17+
18+
let src = PathBuf::from_iter([&rustup_home, "toolchains", &toolchain]);
19+
let dest = PathBuf::from_iter([&rustup_home, "toolchains", name]);
20+
21+
if dest.exists() {
22+
if force {
23+
fs::remove_dir_all(&dest).unwrap();
24+
} else {
25+
println!("{} already exists, pass `--force` to override it", dest.display());
26+
return;
27+
}
28+
}
29+
30+
for entry in WalkDir::new(&src) {
31+
let entry = entry.unwrap();
32+
let relative = entry.path().strip_prefix(&src).unwrap();
33+
34+
if relative.starts_with("bin")
35+
&& matches!(
36+
relative.file_stem().and_then(OsStr::to_str),
37+
Some("cargo-clippy" | "clippy-driver")
38+
)
39+
{
40+
continue;
41+
}
42+
43+
let target = dest.join(relative);
44+
if entry.file_type().is_dir() {
45+
fs::create_dir(&target).unwrap();
46+
} else {
47+
fs::hard_link(entry.path(), target).unwrap();
48+
}
49+
}
50+
51+
symlink_bin("cargo-clippy", &dest, release);
52+
symlink_bin("clippy-driver", &dest, release);
53+
54+
println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`");
55+
println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes");
56+
}
57+
58+
fn symlink_bin(bin: &str, dest: &Path, release: bool) {
59+
#[cfg(windows)]
60+
use std::os::windows::fs::symlink_file as symlink;
61+
62+
#[cfg(not(windows))]
63+
use std::os::unix::fs::symlink;
64+
65+
let profile = if release { "release" } else { "debug" };
66+
let file_name = format!("{bin}{EXE_SUFFIX}");
67+
68+
let mut src = current_dir().unwrap();
69+
src.extend(["target", profile, &file_name]);
70+
71+
let mut dest = dest.to_path_buf();
72+
dest.extend(["bin", &file_name]);
73+
74+
symlink(src, dest).unwrap();
75+
}

0 commit comments

Comments
 (0)