Skip to content

Commit 17f1f02

Browse files
cormacrelfclaude
authored andcommitted
rust-project: support check TARGET, OSS flycheck runnables
Port of facebook#767 by Cormac Relf: - `rust-project check` now accepts a target label (detected by a `:`) in addition to a saved file path - emits flycheck/run/test runnables for OSS rust-analyzer, doing flychecks via `rust-project check {label}` (requires rust-analyzer with `{label}` interpolation support, rust-lang/rust-analyzer#18043) - adds `--use-clippy` to `develop`/`develop-json` - adds `--sysroot-src` and validates the sysroot contains the rust-src component, failing early otherwise - gates the fbcode-only `buck test` runnable behind #[cfg(fbcode_build)] Adapted for this fork: kept CLIENT_METADATA_RUST_PROJECT unconditional (used by every buck command here), threaded use_clippy through the global/first-party extra-cfgs signatures, taught file_from_command about target labels, ungated the TargetOrFile import in scuba.rs, and added the license header to the new target_or_file.rs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LnkZs4NrYWyHv6nREsWeeH Signed-off-by: Austin Seipp <aseipp@pobox.com>
1 parent 637b016 commit 17f1f02

10 files changed

Lines changed: 396 additions & 83 deletions

File tree

integrations/rust-project/src/buck.rs

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub(crate) fn to_project_json(
8989
aliases: FxHashMap<Target, AliasedTargetInfo>,
9090
check_cycles: bool,
9191
include_all_buildfiles: bool,
92+
use_clippy: bool,
9293
global_extra_cfgs: &[String],
9394
first_party_extra_cfgs: &[String],
9495
buck: &Buck,
@@ -261,10 +262,11 @@ pub(crate) fn to_project_json(
261262
check_cycles_in_crate_graph(&crates);
262263
}
263264

264-
let jp = ProjectJson {
265-
sysroot: Box::new(sysroot),
266-
crates,
267-
runnables: vec![Runnable {
265+
let mut runnables = vec![];
266+
267+
#[cfg(fbcode_build)]
268+
{
269+
runnables.extend([Runnable {
268270
program: "buck".to_owned(),
269271
args: vec![
270272
"test".to_owned(),
@@ -276,7 +278,66 @@ pub(crate) fn to_project_json(
276278
],
277279
cwd: project_root.to_owned(),
278280
kind: RunnableKind::TestOne,
279-
}],
281+
}]);
282+
}
283+
284+
// OSS `buck2 test` supports different args, chiefly --test-arg
285+
//
286+
#[cfg(not(fbcode_build))]
287+
{
288+
let rust_project_executable = std::env::args().next().unwrap();
289+
runnables.extend([
290+
Runnable {
291+
kind: RunnableKind::Flycheck,
292+
program: rust_project_executable,
293+
args: {
294+
let mut args = vec!["check".to_owned(), "{label}".to_owned()];
295+
if !use_clippy {
296+
args.push("--use-clippy".to_owned());
297+
args.push("false".to_owned());
298+
}
299+
args
300+
},
301+
cwd: project_root.clone(),
302+
},
303+
Runnable {
304+
kind: RunnableKind::Run,
305+
program: "buck2".to_string(),
306+
args: vec!["run".to_owned(), "{label}".to_string()],
307+
cwd: project_root.clone(),
308+
},
309+
Runnable {
310+
kind: RunnableKind::TestOne,
311+
program: "buck2".to_string(),
312+
args: vec![
313+
"test".to_owned(),
314+
"-c=client.id=rust-project".to_owned(),
315+
"{label}".to_owned(),
316+
"--".to_owned(),
317+
// R-A substitutes {test_id} with e.g. `mycrate::tests::one`
318+
// --test-arg tells `buck2 test` to pass that string through to
319+
// the test program, which we will assume to be the rust test
320+
// harness. Same overall effect as `cargo test -- mycrate::tests::one`,
321+
//
322+
// But this is a bit less than ideal, because buck will build
323+
// all of the related test binaries, including integration tests.
324+
// We don't know your naming conventions for library tests.
325+
// You might have a rust_test target named `mycrate-test`. So
326+
// we might need a way (probably buck metadata in the library
327+
// target, or just querying the related tests)
328+
// to tell rust-project about these.
329+
"--test-arg".to_owned(),
330+
"{test_id}".to_owned(),
331+
],
332+
cwd: project_root.clone(),
333+
},
334+
]);
335+
}
336+
337+
let jp = ProjectJson {
338+
sysroot: Box::new(sysroot),
339+
crates,
340+
runnables,
280341
// needed to ignore the generated `rust-project.json` in diffs, but including the actual
281342
// string will mark this file as generated
282343
generated: String::from("\x40generated"),
@@ -622,17 +683,10 @@ impl Buck {
622683

623684
command.arg("prelude//rust/rust-analyzer/check.bxl:check");
624685

625-
let mut file_path = saved_file.to_owned();
626-
if !file_path.is_absolute() {
627-
if let Ok(cwd) = std::env::current_dir() {
628-
file_path = cwd.join(saved_file);
629-
}
630-
}
631-
632686
// apply BXL scripts-specific arguments:
633-
command.args(["--", "--file"]);
634-
command.arg(file_path.as_os_str());
635-
687+
command.arg("--");
688+
command.args(["--file"]);
689+
command.arg(saved_file.as_os_str());
636690
command.args(["--use-clippy", &use_clippy.to_string()]);
637691

638692
// Set working directory to the containing directory of the target file.
@@ -642,6 +696,35 @@ impl Buck {
642696
command.current_dir(parent_dir);
643697
}
644698

699+
tracing::debug!(?command, "running bxl");
700+
701+
let output = command.output();
702+
703+
let files = deserialize_output(output, &command)?;
704+
Ok(files)
705+
}
706+
707+
#[instrument(fields(use_clippy, target = %target))]
708+
pub(crate) fn check_target(
709+
&self,
710+
use_clippy: bool,
711+
target: &Target,
712+
) -> Result<CheckOutput, anyhow::Error> {
713+
let mut command = self.command(["bxl"]);
714+
715+
if let Some(mode) = &self.mode {
716+
command.arg(mode);
717+
}
718+
719+
command.arg("prelude//rust/rust-analyzer/check.bxl:check");
720+
721+
command.arg("--");
722+
command.arg("--target");
723+
command.arg(target);
724+
command.args(["--use-clippy", &use_clippy.to_string()]);
725+
726+
tracing::debug!(?command, "running bxl");
727+
645728
let output = command.output();
646729

647730
let files = deserialize_output(output, &command)?;
@@ -671,6 +754,8 @@ impl Buck {
671754
]);
672755
command.args(targets);
673756

757+
tracing::debug!(?command, "running bxl");
758+
674759
let mut res: ExpandedAndResolved = deserialize_file_output(command.output(), &command)?;
675760

676761
res.expanded_targets = res

integrations/rust-project/src/cli.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
mod check;
1212
mod develop;
1313
mod new;
14+
mod target_or_file;
1415

1516
#[derive(Debug, Clone)]
1617
pub(crate) enum Input {
@@ -26,5 +27,6 @@ pub(crate) use develop::Develop;
2627
pub(crate) use develop::develop_with_sysroot;
2728
pub(crate) use new::New;
2829
pub(crate) use new::ProjectKind;
30+
pub(crate) use target_or_file::TargetOrFile;
2931

3032
use crate::target::Target;

integrations/rust-project/src/cli/check.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,42 @@
99
*/
1010

1111
use std::path::Path;
12-
use std::path::PathBuf;
1312
use std::str::FromStr;
1413

1514
use anyhow::Context as _;
1615
use rustc_hash::FxHashSet;
1716

1817
use crate::buck;
1918
use crate::buck::Buck;
19+
use crate::cli::TargetOrFile;
2020
use crate::diagnostics;
21-
use crate::path::safe_canonicalize;
2221

2322
pub(crate) struct Check {
2423
pub(crate) buck: buck::Buck,
2524
pub(crate) use_clippy: bool,
26-
pub(crate) saved_file: PathBuf,
25+
pub(crate) target_or_saved_file: TargetOrFile,
2726
}
2827

2928
impl Check {
30-
pub(crate) fn new(buck: Buck, use_clippy: bool, saved_file: PathBuf) -> Self {
31-
let saved_file = safe_canonicalize(&saved_file);
29+
pub(crate) fn new(buck: Buck, use_clippy: bool, target_or_saved_file: TargetOrFile) -> Self {
30+
let target_or_saved_file = target_or_saved_file.canonicalize();
3231

3332
Self {
3433
buck,
3534
use_clippy,
36-
saved_file,
35+
target_or_saved_file,
3736
}
3837
}
3938

39+
#[tracing::instrument(name = "check", skip_all, fields(target = %self.target_or_saved_file))]
4040
pub(crate) fn run(&self) -> Result<(), anyhow::Error> {
4141
let start = std::time::Instant::now();
4242
let buck = &self.buck;
4343

44-
let check_output = buck.check_saved_file(self.use_clippy, &self.saved_file)?;
44+
let check_output = match &self.target_or_saved_file {
45+
TargetOrFile::Target(target) => buck.check_target(self.use_clippy, target)?,
46+
TargetOrFile::File(saved_file) => buck.check_saved_file(self.use_clippy, saved_file)?,
47+
};
4548

4649
let contents: Vec<String> = check_output
4750
.diagnostic_paths
@@ -60,7 +63,7 @@ impl Check {
6063
println!("{out}");
6164
}
6265

63-
crate::scuba::log_check(start.elapsed(), &self.saved_file, self.use_clippy);
66+
crate::scuba::log_check(start.elapsed(), &self.target_or_saved_file, self.use_clippy);
6467

6568
Ok(())
6669
}

0 commit comments

Comments
 (0)